From 8dbb76099fc4a367a287fe34d4a23142f51b3c1f Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Sun, 14 Jan 2024 11:47:02 +0100 Subject: [PATCH] Migrate to file-scoped namespaces --- build/Build.cs | 2 +- docs/Build.cs | 33 +- samples/NetVips.Samples/ISample.cs | 13 +- samples/NetVips.Samples/Program.cs | 157 +- samples/NetVips.Samples/SampleExtensions.cs | 99 +- samples/NetVips.Samples/Samples/Canny.cs | 39 +- .../Samples/CaptchaGenerator.cs | 203 +- samples/NetVips.Samples/Samples/Combine.cs | 63 +- samples/NetVips.Samples/Samples/Duotone.cs | 107 +- .../Samples/EmbedMultiplyConv.cs | 69 +- samples/NetVips.Samples/Samples/Emboss.cs | 95 +- samples/NetVips.Samples/Samples/GdiConvert.cs | 143 +- .../NetVips.Samples/Samples/GenerateEnums.cs | 140 +- .../Samples/GenerateImageClass.cs | 1373 +- .../Samples/GenerateImageOperators.cs | 654 +- samples/NetVips.Samples/Samples/HelloWorld.cs | 29 +- .../Samples/IdentifyExtension.cs | 123 +- .../NetVips.Samples/Samples/ImagePyramid.cs | 67 +- samples/NetVips.Samples/Samples/Indexer.cs | 55 +- samples/NetVips.Samples/Samples/LeakTest.cs | 107 +- .../NetVips.Samples/Samples/MutableImage.cs | 35 +- .../NetVips.Samples/Samples/NetworkStream.cs | 77 +- .../Samples/NewFromMemoryRef.cs | 47 +- .../Samples/NewFromStreamRef.cs | 53 +- .../NetVips.Samples/Samples/OnePointMosaic.cs | 153 +- .../NetVips.Samples/Samples/OperationBlock.cs | 57 +- .../NetVips.Samples/Samples/OperationRef.cs | 87 +- samples/NetVips.Samples/Samples/PostClose.cs | 53 +- samples/NetVips.Samples/Samples/Progress.cs | 99 +- .../NetVips.Samples/Samples/RandomCropper.cs | 69 +- .../NetVips.Samples/Samples/ShapeCropping.cs | 545 +- samples/NetVips.Samples/Samples/Smartcrop.cs | 27 +- samples/NetVips.Samples/Samples/Sobel.cs | 35 +- samples/NetVips.Samples/Samples/Stream.cs | 35 +- samples/NetVips.Samples/Samples/Thumbnail.cs | 27 +- .../Samples/ThumbnailPipeline.cs | 365 +- .../Samples/ThumbnailStream.cs | 35 +- src/NetVips.Extensions/BitmapConverter.cs | 688 +- src/NetVips/Cache.cs | 79 +- src/NetVips/Connection.cs | 53 +- src/NetVips/Enums.cs | 2015 +- src/NetVips/ExtensionMethods.cs | 473 +- src/NetVips/GObject.cs | 332 +- src/NetVips/GValue.cs | 889 +- src/NetVips/Image.Generated.cs | 23203 ++++++++-------- src/NetVips/Image.Operators.cs | 1721 +- src/NetVips/Image.cs | 4195 ++- src/NetVips/Internal/Enums.cs | 71 +- src/NetVips/Internal/GLib.cs | 90 +- src/NetVips/Internal/GObject.cs | 486 +- src/NetVips/Internal/Vips.cs | 1308 +- src/NetVips/Interop/Libraries.cs | 21 +- src/NetVips/Interpolate.cs | 69 +- src/NetVips/Introspect.cs | 323 +- src/NetVips/Log.cs | 237 +- src/NetVips/ModuleInitializer.cs | 191 +- src/NetVips/ModuleInitializerAttribute.cs | 11 +- src/NetVips/MutableImage.Generated.cs | 561 +- src/NetVips/MutableImage.cs | 248 +- src/NetVips/NetVips.cs | 945 +- src/NetVips/Operation.cs | 433 +- src/NetVips/Region.cs | 111 +- src/NetVips/Source.cs | 245 +- src/NetVips/SourceCustom.cs | 191 +- src/NetVips/SourceStream.cs | 139 +- src/NetVips/Stats.cs | 61 +- src/NetVips/Target.cs | 165 +- src/NetVips/TargetCustom.cs | 313 +- src/NetVips/TargetStream.cs | 213 +- src/NetVips/VOption.cs | 189 +- src/NetVips/VipsBlob.cs | 105 +- src/NetVips/VipsException.cs | 71 +- src/NetVips/VipsObject.cs | 261 +- src/NetVips/VipsProgress.cs | 107 +- tests/NetVips.Benchmarks/Benchmark.cs | 329 +- tests/NetVips.Benchmarks/Config.cs | 68 +- .../ImageSharp/ConvolutionProcessor.cs | 91 +- tests/NetVips.Benchmarks/Runner.cs | 19 +- tests/NetVips.Benchmarks/TestImage.cs | 61 +- tests/NetVips.Tests/ArithmeticTests.cs | 1803 +- tests/NetVips.Tests/ColourTests.cs | 395 +- tests/NetVips.Tests/ConnectionTests.cs | 229 +- tests/NetVips.Tests/ConversionTests.cs | 1775 +- tests/NetVips.Tests/ConvolutionTests.cs | 451 +- tests/NetVips.Tests/CreateTests.cs | 1043 +- tests/NetVips.Tests/DrawTests.cs | 219 +- tests/NetVips.Tests/ExtensionsTests.cs | 150 +- tests/NetVips.Tests/ForeignTests.cs | 2399 +- tests/NetVips.Tests/GValueTests.cs | 341 +- tests/NetVips.Tests/Helper.cs | 811 +- tests/NetVips.Tests/HistogramTests.cs | 269 +- tests/NetVips.Tests/IoFuncsTests.cs | 403 +- tests/NetVips.Tests/MorphologyTests.cs | 133 +- tests/NetVips.Tests/ResampleTests.cs | 571 +- tests/NetVips.Tests/TestsFixture.cs | 37 +- 95 files changed, 28844 insertions(+), 28936 deletions(-) diff --git a/build/Build.cs b/build/Build.cs index 6d8ffd29..af3dfa3d 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -10,8 +10,8 @@ using Nuke.Common.Tools.DotNet; using Nuke.Common.Tools.NuGet; using Nuke.Common.Utilities.Collections; -using static Nuke.Common.Tools.NuGet.NuGetTasks; using static Nuke.Common.Tools.DotNet.DotNetTasks; +using static Nuke.Common.Tools.NuGet.NuGetTasks; [UnsetVisualStudioEnvironmentVariables] partial class Build : NukeBuild diff --git a/docs/Build.cs b/docs/Build.cs index aef33586..7c0374a2 100644 --- a/docs/Build.cs +++ b/docs/Build.cs @@ -1,22 +1,21 @@ -namespace NetVips.Docs -{ - using System; - using System.IO; - using System.Threading.Tasks; - using Docfx.Dotnet; +using System; +using System.IO; +using System.Threading.Tasks; +using Docfx.Dotnet; + +namespace NetVips.Docs; - class Build +class Build +{ + static async Task Main(string[] args) { - static async Task Main(string[] args) - { - var projectDir = - Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\..")); - var currentDirectory = Directory.GetCurrentDirectory(); + var projectDir = + Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\..")); + var currentDirectory = Directory.GetCurrentDirectory(); - Directory.SetCurrentDirectory(projectDir); - await DotnetApiCatalog.GenerateManagedReferenceYamlFiles("docfx.json"); - await Docfx.Docset.Build("docfx.json"); - Directory.SetCurrentDirectory(currentDirectory); - } + Directory.SetCurrentDirectory(projectDir); + await DotnetApiCatalog.GenerateManagedReferenceYamlFiles("docfx.json"); + await Docfx.Docset.Build("docfx.json"); + Directory.SetCurrentDirectory(currentDirectory); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/ISample.cs b/samples/NetVips.Samples/ISample.cs index 42a77605..6862a0d1 100644 --- a/samples/NetVips.Samples/ISample.cs +++ b/samples/NetVips.Samples/ISample.cs @@ -1,11 +1,10 @@ -namespace NetVips +namespace NetVips; + +public interface ISample { - public interface ISample - { - string Name { get; } + string Name { get; } - string Category { get; } + string Category { get; } - void Execute(string[] args); - } + void Execute(string[] args); } \ No newline at end of file diff --git a/samples/NetVips.Samples/Program.cs b/samples/NetVips.Samples/Program.cs index ff62eebf..0090ae0e 100644 --- a/samples/NetVips.Samples/Program.cs +++ b/samples/NetVips.Samples/Program.cs @@ -1,104 +1,103 @@ -namespace NetVips +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace NetVips; + +class Program { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using System.Text; + private static readonly List Samples = Assembly.GetExecutingAssembly().GetTypes() + .Where(x => x.GetInterfaces().Contains(typeof(ISample)) && x.GetConstructor(Type.EmptyTypes) != null) + .Select(x => Activator.CreateInstance(x) as ISample) + .OrderBy(s => s?.Category) + .ToList(); - class Program + static void Main(string[] args) { - private static readonly List Samples = Assembly.GetExecutingAssembly().GetTypes() - .Where(x => x.GetInterfaces().Contains(typeof(ISample)) && x.GetConstructor(Type.EmptyTypes) != null) - .Select(x => Activator.CreateInstance(x) as ISample) - .OrderBy(s => s?.Category) - .ToList(); - - static void Main(string[] args) + if (!ModuleInitializer.VipsInitialized) { - if (!ModuleInitializer.VipsInitialized) - { - Console.WriteLine("Error: Unable to init libvips. Please check your PATH environment variable."); - Console.ReadLine(); - return; - } + Console.WriteLine("Error: Unable to init libvips. Please check your PATH environment variable."); + Console.ReadLine(); + return; + } - Console.WriteLine($"libvips {NetVips.Version(0)}.{NetVips.Version(1)}.{NetVips.Version(2)}"); + Console.WriteLine($"libvips {NetVips.Version(0)}.{NetVips.Version(1)}.{NetVips.Version(2)}"); - Console.WriteLine( - $"Type a number (1-{Samples.Count}) to execute a sample of your choice. Press or type 'Q' to quit."); + Console.WriteLine( + $"Type a number (1-{Samples.Count}) to execute a sample of your choice. Press or type 'Q' to quit."); - DisplayMenu(); + DisplayMenu(); - string input; - do + string input; + do + { + var sampleArgs = Array.Empty(); + if (args.Length > 0) { - var sampleArgs = Array.Empty(); - if (args.Length > 0) - { - var sampleId = Samples.Select((value, index) => new { Index = index + 1, value.Name }) - .FirstOrDefault(s => s.Name.Equals(args[0]))?.Index; - input = sampleId != null ? $"{sampleId}" : "0"; - sampleArgs = args.Skip(1).ToArray(); - } - else - { - input = Console.ReadLine(); - } + var sampleId = Samples.Select((value, index) => new { Index = index + 1, value.Name }) + .FirstOrDefault(s => s.Name.Equals(args[0]))?.Index; + input = sampleId != null ? $"{sampleId}" : "0"; + sampleArgs = args.Skip(1).ToArray(); + } + else + { + input = Console.ReadLine(); + } - if (int.TryParse(input, out var userChoice)) + if (int.TryParse(input, out var userChoice)) + { + if (!TryGetSample(userChoice, out var sample)) { - if (!TryGetSample(userChoice, out var sample)) - { - Console.WriteLine("Sample doesn't exists, try again"); - continue; - } - - Console.WriteLine($"Executing sample: {sample.Name}"); - sample.Execute(sampleArgs); - Console.WriteLine("Sample successfully executed!"); + Console.WriteLine("Sample doesn't exists, try again"); + continue; } - // Clear any arguments - args = Array.Empty(); - } while (!string.IsNullOrEmpty(input) && !string.Equals(input, "Q", StringComparison.OrdinalIgnoreCase)); - } + Console.WriteLine($"Executing sample: {sample.Name}"); + sample.Execute(sampleArgs); + Console.WriteLine("Sample successfully executed!"); + } - public static void DisplayMenu() - { - Console.WriteLine(); - Console.WriteLine("Menu:"); + // Clear any arguments + args = Array.Empty(); + } while (!string.IsNullOrEmpty(input) && !string.Equals(input, "Q", StringComparison.OrdinalIgnoreCase)); + } + + public static void DisplayMenu() + { + Console.WriteLine(); + Console.WriteLine("Menu:"); - string currCategory = null; - var menu = Samples.Select((value, index) => new { Index = index + 1, value.Name, value.Category }) - .Aggregate(new StringBuilder(), (builder, pair) => + string currCategory = null; + var menu = Samples.Select((value, index) => new { Index = index + 1, value.Name, value.Category }) + .Aggregate(new StringBuilder(), (builder, pair) => + { + if (currCategory != pair.Category) { - if (currCategory != pair.Category) + if (pair.Index > 1) { - if (pair.Index > 1) - { - builder.AppendLine(); - } + builder.AppendLine(); + } - builder.AppendLine($" - {pair.Category}"); + builder.AppendLine($" - {pair.Category}"); - currCategory = pair.Category; - } + currCategory = pair.Category; + } - builder.AppendLine($" {pair.Index}: {pair.Name}"); - return builder; - }); + builder.AppendLine($" {pair.Index}: {pair.Name}"); + return builder; + }); - Console.WriteLine(menu); - } + Console.WriteLine(menu); + } - public static bool TryGetSample(int id, out ISample sample) - { - sample = Samples - .Select((value, index) => new { Index = index + 1, Sample = value }) - .FirstOrDefault(pair => pair.Index == id)?.Sample; + public static bool TryGetSample(int id, out ISample sample) + { + sample = Samples + .Select((value, index) => new { Index = index + 1, Sample = value }) + .FirstOrDefault(pair => pair.Index == id)?.Sample; - return sample != null; - } + return sample != null; } } \ No newline at end of file diff --git a/samples/NetVips.Samples/SampleExtensions.cs b/samples/NetVips.Samples/SampleExtensions.cs index 2275aef3..edc65d6d 100644 --- a/samples/NetVips.Samples/SampleExtensions.cs +++ b/samples/NetVips.Samples/SampleExtensions.cs @@ -1,65 +1,64 @@ -namespace NetVips -{ - using System; - using System.Linq; +using System; +using System.Linq; + +namespace NetVips; - public static class SampleExtensions +public static class SampleExtensions +{ + /// + /// Make first letter of a string upper case. + /// + /// The input string. + /// A new string with the first letter upper case. + internal static string FirstLetterToUpper(this string str) { - /// - /// Make first letter of a string upper case. - /// - /// The input string. - /// A new string with the first letter upper case. - internal static string FirstLetterToUpper(this string str) + if (str == null) { - if (str == null) - { - return null; - } - - if (str.Length > 1) - { - return char.ToUpper(str[0]) + str[1..]; - } - - return str.ToUpper(); + return null; } - /// - /// Make first letter of a string lower case. - /// - /// The input string. - /// A new string with the first letter lower case. - internal static string FirstLetterToLower(this string str) + if (str.Length > 1) { - if (str == null) - { - return null; - } - - if (str.Length > 1) - { - return char.ToLower(str[0]) + str[1..]; - } - - return str.ToLower(); + return char.ToUpper(str[0]) + str[1..]; } - /// - /// Convert snake case (my_string) to camel case (MyString). - /// - /// The input string. - /// A new camel cased string. - internal static string ToPascalCase(this string str) + return str.ToUpper(); + } + + /// + /// Make first letter of a string lower case. + /// + /// The input string. + /// A new string with the first letter lower case. + internal static string FirstLetterToLower(this string str) + { + if (str == null) { - return str.Split(new[] { "_" }, StringSplitOptions.RemoveEmptyEntries) - .Select(s => char.ToUpperInvariant(s[0]) + s[1..]) - .Aggregate(string.Empty, (s1, s2) => s1 + s2); + return null; } - public static double NextDouble(this Random random, double minValue, double maxValue) + if (str.Length > 1) { - return random.NextDouble() * (maxValue - minValue) + minValue; + return char.ToLower(str[0]) + str[1..]; } + + return str.ToLower(); + } + + /// + /// Convert snake case (my_string) to camel case (MyString). + /// + /// The input string. + /// A new camel cased string. + internal static string ToPascalCase(this string str) + { + return str.Split(new[] { "_" }, StringSplitOptions.RemoveEmptyEntries) + .Select(s => char.ToUpperInvariant(s[0]) + s[1..]) + .Aggregate(string.Empty, (s1, s2) => s1 + s2); + } + + public static double NextDouble(this Random random, double minValue, double maxValue) + { + return random.NextDouble() * (maxValue - minValue) + minValue; } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/Canny.cs b/samples/NetVips.Samples/Samples/Canny.cs index 1ffad500..9cbc41bf 100644 --- a/samples/NetVips.Samples/Samples/Canny.cs +++ b/samples/NetVips.Samples/Samples/Canny.cs @@ -1,30 +1,29 @@ -namespace NetVips.Samples -{ - using System; +using System; - public class Canny : ISample - { - public string Name => "Canny"; - public string Category => "Edge detection"; +namespace NetVips.Samples; - public const string Filename = "images/lichtenstein.jpg"; +public class Canny : ISample +{ + public string Name => "Canny"; + public string Category => "Edge detection"; - public void Execute(string[] args) - { - using var im = Image.NewFromFile(Filename, access: Enums.Access.Sequential); + public const string Filename = "images/lichtenstein.jpg"; + + public void Execute(string[] args) + { + using var im = Image.NewFromFile(Filename, access: Enums.Access.Sequential); - // Optionally, convert to greyscale - //using var mono = im.Colourspace(Enums.Interpretation.Bw); + // Optionally, convert to greyscale + //using var mono = im.Colourspace(Enums.Interpretation.Bw); - // Canny edge detector - using var canny = /*mono*/im.Canny(1.4, precision: Enums.Precision.Integer); + // Canny edge detector + using var canny = /*mono*/im.Canny(1.4, precision: Enums.Precision.Integer); - // Canny makes a float image, scale the output up to make it visible. - using var scale = canny * 64; + // Canny makes a float image, scale the output up to make it visible. + using var scale = canny * 64; - scale.WriteToFile("canny.jpg"); + scale.WriteToFile("canny.jpg"); - Console.WriteLine("See canny.jpg"); - } + Console.WriteLine("See canny.jpg"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/CaptchaGenerator.cs b/samples/NetVips.Samples/Samples/CaptchaGenerator.cs index 88675208..16e41618 100644 --- a/samples/NetVips.Samples/Samples/CaptchaGenerator.cs +++ b/samples/NetVips.Samples/Samples/CaptchaGenerator.cs @@ -1,129 +1,128 @@ -namespace NetVips.Samples +using System; +using System.Linq; + +namespace NetVips.Samples; + +/// +/// From: https://github.com/libvips/libvips/issues/898 +/// +public class CaptchaGenerator : ISample { - using System; - using System.Linq; + public string Name => "Captcha generator"; + public string Category => "Create"; + + public const string Text = "Hello World"; /// - /// From: https://github.com/libvips/libvips/issues/898 + /// A warp image is a 2D grid containing the new coordinates of each pixel with + /// the new x in band 0 and the new y in band 1 + /// + /// you can also use a complex image + /// + /// start from a low-res XY image and distort it /// - public class CaptchaGenerator : ISample + /// to wobble. + /// A new . + public Image Wobble(Image image) { - public string Name => "Captcha generator"; - public string Category => "Create"; - - public const string Text = "Hello World"; - - /// - /// A warp image is a 2D grid containing the new coordinates of each pixel with - /// the new x in band 0 and the new y in band 1 - /// - /// you can also use a complex image - /// - /// start from a low-res XY image and distort it - /// - /// to wobble. - /// A new . - public Image Wobble(Image image) - { - using var xy = Image.Xyz(image.Width / 20, image.Height / 20); - using var xDistort = Image.Gaussnoise(xy.Width, xy.Height); - using var yDistort = Image.Gaussnoise(xy.Width, xy.Height); - using var join = xDistort.Bandjoin(yDistort); - using var divide = join / 150; - using var add = xy + divide; - using var resize = add.Resize(20); - using var scale = resize * 20; - - // apply the warp - return image.Mapim(scale); - } + using var xy = Image.Xyz(image.Width / 20, image.Height / 20); + using var xDistort = Image.Gaussnoise(xy.Width, xy.Height); + using var yDistort = Image.Gaussnoise(xy.Width, xy.Height); + using var join = xDistort.Bandjoin(yDistort); + using var divide = join / 150; + using var add = xy + divide; + using var resize = add.Resize(20); + using var scale = resize * 20; + + // apply the warp + return image.Mapim(scale); + } - public void Execute(string[] args) - { - var random = new Random(); + public void Execute(string[] args) + { + var random = new Random(); - var textLayer = Image.Black(1, 1); - var xPosition = 0; + var textLayer = Image.Black(1, 1); + var xPosition = 0; - foreach (var c in Text) + foreach (var c in Text) + { + if (c == ' ') { - if (c == ' ') - { - xPosition += 50; - continue; - } - - using var letter = Image.Text(c.ToString(), dpi: 600); - - using var image = letter.Gravity(Enums.CompassDirection.Centre, letter.Width + 50, letter.Height + 50); + xPosition += 50; + continue; + } - // random scale and rotate - using var similarity = image.Similarity(scale: random.NextDouble(0, 0.2) + 0.8, - angle: random.Next(0, 40) - 20); + using var letter = Image.Text(c.ToString(), dpi: 600); - // random wobble - using var wobble = Wobble(similarity); + using var image = letter.Gravity(Enums.CompassDirection.Centre, letter.Width + 50, letter.Height + 50); - // random colour - var colour = Enumerable.Range(1, 3).Select(_ => random.Next(0, 255)).ToArray(); - using var ifthenelse = wobble.Ifthenelse(colour, 0, blend: true); + // random scale and rotate + using var similarity = image.Similarity(scale: random.NextDouble(0, 0.2) + 0.8, + angle: random.Next(0, 40) - 20); - // tag as 9-bit srgb - using var srgb = ifthenelse.Copy(interpretation: Enums.Interpretation.Srgb); - using var cast = srgb.Cast(Enums.BandFormat.Uchar); + // random wobble + using var wobble = Wobble(similarity); - // position at our write position in the image - using var embed = cast.Embed(xPosition, 0, image.Width + xPosition, image.Height); + // random colour + var colour = Enumerable.Range(1, 3).Select(_ => random.Next(0, 255)).ToArray(); + using var ifthenelse = wobble.Ifthenelse(colour, 0, blend: true); - using (textLayer) - { - using var add = textLayer + embed; - textLayer = add.Cast(Enums.BandFormat.Uchar); - } + // tag as 9-bit srgb + using var srgb = ifthenelse.Copy(interpretation: Enums.Interpretation.Srgb); + using var cast = srgb.Cast(Enums.BandFormat.Uchar); - xPosition += letter.Width; - } + // position at our write position in the image + using var embed = cast.Embed(xPosition, 0, image.Width + xPosition, image.Height); - // remove any unused edges - var trim = textLayer.FindTrim(background: new double[] { 0 }); using (textLayer) { - textLayer = textLayer.Crop((int)trim[0], (int)trim[1], (int)trim[2], (int)trim[3]); + using var add = textLayer + embed; + textLayer = add.Cast(Enums.BandFormat.Uchar); } - // make an alpha for the text layer: just a mono version of the image, but scaled - // up so the letters themselves are not transparent - using var mono = textLayer.Colourspace(Enums.Interpretation.Bw); - using var scale = mono * 3; - using var alpha = scale.Cast(Enums.BandFormat.Uchar); - using (textLayer) - { - textLayer = textLayer.Bandjoin(alpha); - } + xPosition += letter.Width; + } - // make a white background with random speckles - using var speckles = Image.Gaussnoise(textLayer.Width, textLayer.Height, mean: 400, sigma: 200); - using var background = Enumerable.Range(1, 2).Aggregate(speckles, - (a, _) => - { - using (a) - { - using var speckles2 = - Image.Gaussnoise(textLayer.Width, textLayer.Height, mean: 400, sigma: 200); - using var join = a.Bandjoin(speckles2); - using var srgb = join.Copy(interpretation: Enums.Interpretation.Srgb); - return srgb.Cast(Enums.BandFormat.Uchar); - } - }); - - // composite the text over the background - using (textLayer) + // remove any unused edges + var trim = textLayer.FindTrim(background: new double[] { 0 }); + using (textLayer) + { + textLayer = textLayer.Crop((int)trim[0], (int)trim[1], (int)trim[2], (int)trim[3]); + } + + // make an alpha for the text layer: just a mono version of the image, but scaled + // up so the letters themselves are not transparent + using var mono = textLayer.Colourspace(Enums.Interpretation.Bw); + using var scale = mono * 3; + using var alpha = scale.Cast(Enums.BandFormat.Uchar); + using (textLayer) + { + textLayer = textLayer.Bandjoin(alpha); + } + + // make a white background with random speckles + using var speckles = Image.Gaussnoise(textLayer.Width, textLayer.Height, mean: 400, sigma: 200); + using var background = Enumerable.Range(1, 2).Aggregate(speckles, + (a, _) => { - using var final = background.Composite(textLayer, Enums.BlendMode.Over); - final.WriteToFile("captcha.jpg"); - } + using (a) + { + using var speckles2 = + Image.Gaussnoise(textLayer.Width, textLayer.Height, mean: 400, sigma: 200); + using var join = a.Bandjoin(speckles2); + using var srgb = join.Copy(interpretation: Enums.Interpretation.Srgb); + return srgb.Cast(Enums.BandFormat.Uchar); + } + }); - Console.WriteLine("See captcha.jpg"); + // composite the text over the background + using (textLayer) + { + using var final = background.Composite(textLayer, Enums.BlendMode.Over); + final.WriteToFile("captcha.jpg"); } + + Console.WriteLine("See captcha.jpg"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/Combine.cs b/samples/NetVips.Samples/Samples/Combine.cs index 133788ad..caad104a 100644 --- a/samples/NetVips.Samples/Samples/Combine.cs +++ b/samples/NetVips.Samples/Samples/Combine.cs @@ -1,44 +1,43 @@ -namespace NetVips.Samples -{ - using System; +using System; - /// - /// From: https://github.com/libvips/lua-vips/blob/master/example/combine.lua - /// - public class Combine : ISample - { - public string Name => "Combine"; - public string Category => "Conversion"; +namespace NetVips.Samples; - public const string MainFilename = "images/Gugg_coloured.jpg"; - public const string WatermarkFilename = "images/PNG_transparency_demonstration_1.png"; +/// +/// From: https://github.com/libvips/lua-vips/blob/master/example/combine.lua +/// +public class Combine : ISample +{ + public string Name => "Combine"; + public string Category => "Conversion"; - public const int Left = 100; - public const int Top = 100; + public const string MainFilename = "images/Gugg_coloured.jpg"; + public const string WatermarkFilename = "images/PNG_transparency_demonstration_1.png"; - public void Execute(string[] args) - { - using var main = Image.NewFromFile(MainFilename, access: Enums.Access.Sequential); - using var watermark = Image.NewFromFile(WatermarkFilename, access: Enums.Access.Sequential); + public const int Left = 100; + public const int Top = 100; + + public void Execute(string[] args) + { + using var main = Image.NewFromFile(MainFilename, access: Enums.Access.Sequential); + using var watermark = Image.NewFromFile(WatermarkFilename, access: Enums.Access.Sequential); - var width = watermark.Width; - var height = watermark.Height; + var width = watermark.Width; + var height = watermark.Height; - // extract related area from main image - using var baseImage = main.Crop(Left, Top, width, height); + // extract related area from main image + using var baseImage = main.Crop(Left, Top, width, height); - // composite the two areas using the PDF "over" mode - using var composite = baseImage.Composite(watermark, Enums.BlendMode.Over); + // composite the two areas using the PDF "over" mode + using var composite = baseImage.Composite(watermark, Enums.BlendMode.Over); - // the result will have an alpha, and our base image does not .. we must flatten - // out the alpha before we can insert it back into a plain RGB JPG image - using var rgb = composite.Flatten(); + // the result will have an alpha, and our base image does not .. we must flatten + // out the alpha before we can insert it back into a plain RGB JPG image + using var rgb = composite.Flatten(); - // insert composite back in to main image on related area - using var combined = main.Insert(rgb, Left, Top); - combined.WriteToFile("combine.jpg"); + // insert composite back in to main image on related area + using var combined = main.Insert(rgb, Left, Top); + combined.WriteToFile("combine.jpg"); - Console.WriteLine("See combine.jpg"); - } + Console.WriteLine("See combine.jpg"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/Duotone.cs b/samples/NetVips.Samples/Samples/Duotone.cs index 34f6608b..4babfb98 100644 --- a/samples/NetVips.Samples/Samples/Duotone.cs +++ b/samples/NetVips.Samples/Samples/Duotone.cs @@ -1,70 +1,69 @@ -namespace NetVips.Samples -{ - using System; +using System; - /// - /// From: https://github.com/lovell/sharp/issues/1235#issuecomment-390907151 - /// - public class Duotone : ISample - { - public string Name => "Duotone"; - public string Category => "Filter"; +namespace NetVips.Samples; - public const string Filename = "images/equus_quagga.jpg"; +/// +/// From: https://github.com/lovell/sharp/issues/1235#issuecomment-390907151 +/// +public class Duotone : ISample +{ + public string Name => "Duotone"; + public string Category => "Filter"; - // #C83658 as CIELAB triple - public double[] Start = { 46.479, 58.976, 15.052 }; + public const string Filename = "images/equus_quagga.jpg"; - // #D8E74F as CIELAB triple - public double[] Stop = { 88.12, -23.952, 69.178 }; + // #C83658 as CIELAB triple + public double[] Start = { 46.479, 58.976, 15.052 }; - public void Execute(string[] args) - { - // Makes a lut which is a smooth gradient from start colour to stop colour, - // with start and stop in CIELAB - using var identity = Image.Identity(); - using var index = identity / 255; - using var stop = index * Stop; - using var inverse = 1 - index; - using var start = inverse * Start; - using var gradient = stop + start; - using var lut = gradient.Colourspace(Enums.Interpretation.Srgb, sourceSpace: Enums.Interpretation.Lab); + // #D8E74F as CIELAB triple + public double[] Stop = { 88.12, -23.952, 69.178 }; - var im = Image.NewFromFile(Filename, access: Enums.Access.Sequential); + public void Execute(string[] args) + { + // Makes a lut which is a smooth gradient from start colour to stop colour, + // with start and stop in CIELAB + using var identity = Image.Identity(); + using var index = identity / 255; + using var stop = index * Stop; + using var inverse = 1 - index; + using var start = inverse * Start; + using var gradient = stop + start; + using var lut = gradient.Colourspace(Enums.Interpretation.Srgb, sourceSpace: Enums.Interpretation.Lab); - // The first step to implement a duotone filter is to convert the - // image to greyscale. The image is then mapped through the lut. - // Mapping is done by looping over the image and looking up each - // pixel value in the lut and replacing it with the pre-calculated - // result. - if (im.HasAlpha()) - { - // Separate alpha channel - using var withoutAlpha = im.ExtractBand(0, im.Bands - 1); - using var alpha = im[im.Bands - 1]; - using var mono = withoutAlpha.Colourspace(Enums.Interpretation.Bw); - using var mapped = mono.Maplut(lut); - using (im) - { - im = mapped.Bandjoin(alpha); - } - } - else + var im = Image.NewFromFile(Filename, access: Enums.Access.Sequential); + + // The first step to implement a duotone filter is to convert the + // image to greyscale. The image is then mapped through the lut. + // Mapping is done by looping over the image and looking up each + // pixel value in the lut and replacing it with the pre-calculated + // result. + if (im.HasAlpha()) + { + // Separate alpha channel + using var withoutAlpha = im.ExtractBand(0, im.Bands - 1); + using var alpha = im[im.Bands - 1]; + using var mono = withoutAlpha.Colourspace(Enums.Interpretation.Bw); + using var mapped = mono.Maplut(lut); + using (im) { - using var mono = im.Colourspace(Enums.Interpretation.Bw); - using (im) - { - im = mono.Maplut(lut); - } + im = mapped.Bandjoin(alpha); } - + } + else + { + using var mono = im.Colourspace(Enums.Interpretation.Bw); using (im) { - // Finally, write the result back to a file on disk - im.WriteToFile("duotone.jpg"); + im = mono.Maplut(lut); } + } - Console.WriteLine("See duotone.jpg"); + using (im) + { + // Finally, write the result back to a file on disk + im.WriteToFile("duotone.jpg"); } + + Console.WriteLine("See duotone.jpg"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/EmbedMultiplyConv.cs b/samples/NetVips.Samples/Samples/EmbedMultiplyConv.cs index c308005a..aaac1dff 100644 --- a/samples/NetVips.Samples/Samples/EmbedMultiplyConv.cs +++ b/samples/NetVips.Samples/Samples/EmbedMultiplyConv.cs @@ -1,43 +1,42 @@ -namespace NetVips.Samples +using System; + +namespace NetVips.Samples; + +/// +/// From: https://github.com/libvips/ruby-vips#example +/// +public class EmbedMultiplyConv : ISample { - using System; + public string Name => "Embed / Multiply / Convolution"; + public string Category => "Other"; + + public const string Filename = "images/lichtenstein.jpg"; - /// - /// From: https://github.com/libvips/ruby-vips#example - /// - public class EmbedMultiplyConv : ISample + public void Execute(string[] args) { - public string Name => "Embed / Multiply / Convolution"; - public string Category => "Other"; + using var im = Image.NewFromFile(Filename); - public const string Filename = "images/lichtenstein.jpg"; + // put im at position (100, 100) in a 3000 x 3000 pixel image, + // make the other pixels in the image by mirroring im up / down / + // left / right, see + // https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-embed + using var embed = im.Embed(100, 100, 3000, 3000, extend: Enums.Extend.Mirror); - public void Execute(string[] args) + // multiply the green (middle) band by 2, leave the other two alone + using var multiply = embed * new[] { 1, 2, 1 }; + + // make an image from an array constant, convolve with it + using var mask = Image.NewFromArray(new[,] { - using var im = Image.NewFromFile(Filename); - - // put im at position (100, 100) in a 3000 x 3000 pixel image, - // make the other pixels in the image by mirroring im up / down / - // left / right, see - // https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-embed - using var embed = im.Embed(100, 100, 3000, 3000, extend: Enums.Extend.Mirror); - - // multiply the green (middle) band by 2, leave the other two alone - using var multiply = embed * new[] { 1, 2, 1 }; - - // make an image from an array constant, convolve with it - using var mask = Image.NewFromArray(new[,] - { - {-1, -1, -1}, - {-1, 16, -1}, - {-1, -1, -1} - }, 8); - using var convolve = multiply.Conv(mask, precision: Enums.Precision.Integer); - - // finally, write the result back to a file on disk - convolve.WriteToFile("embed-multiply-conv.jpg"); - - Console.WriteLine("See embed-multiply-conv.jpg"); - } + {-1, -1, -1}, + {-1, 16, -1}, + {-1, -1, -1} + }, 8); + using var convolve = multiply.Conv(mask, precision: Enums.Precision.Integer); + + // finally, write the result back to a file on disk + convolve.WriteToFile("embed-multiply-conv.jpg"); + + Console.WriteLine("See embed-multiply-conv.jpg"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/Emboss.cs b/samples/NetVips.Samples/Samples/Emboss.cs index 5469c207..12b94e8e 100644 --- a/samples/NetVips.Samples/Samples/Emboss.cs +++ b/samples/NetVips.Samples/Samples/Emboss.cs @@ -1,56 +1,55 @@ -namespace NetVips.Samples +using System; + +namespace NetVips.Samples; + +public class Emboss : ISample { - using System; + public string Name => "Emboss"; + public string Category => "Filter"; - public class Emboss : ISample + public const string Filename = "images/lichtenstein.jpg"; + + public void Execute(string[] args) { - public string Name => "Emboss"; - public string Category => "Filter"; + using var im = Image.NewFromFile(Filename); - public const string Filename = "images/lichtenstein.jpg"; + // Optionally, Convert the image to greyscale + //using var mono = im.Colourspace(Enums.Interpretation.Bw); - public void Execute(string[] args) + // The four primary emboss kernels. + // Offset the pixel values by 128 to achieve the emboss effect. + using var kernel1 = Image.NewFromArray(new[,] + { + {0, 1, 0}, + {0, 0, 0}, + {0, -1, 0} + }, offset: 128); + using var kernel2 = Image.NewFromArray(new[,] { - using var im = Image.NewFromFile(Filename); - - // Optionally, Convert the image to greyscale - //using var mono = im.Colourspace(Enums.Interpretation.Bw); - - // The four primary emboss kernels. - // Offset the pixel values by 128 to achieve the emboss effect. - using var kernel1 = Image.NewFromArray(new[,] - { - {0, 1, 0}, - {0, 0, 0}, - {0, -1, 0} - }, offset: 128); - using var kernel2 = Image.NewFromArray(new[,] - { - {1, 0, 0}, - {0, 0, 0}, - {0, 0, -1} - }, offset: 128); - using var kernel3 = kernel1.Rot270(); - using var kernel4 = kernel2.Rot90(); - - // Apply the emboss kernels - using var conv1 = /*mono*/im.Conv(kernel1, precision: Enums.Precision.Float); - using var conv2 = /*mono*/im.Conv(kernel2, precision: Enums.Precision.Float); - using var conv3 = /*mono*/im.Conv(kernel3, precision: Enums.Precision.Float); - using var conv4 = /*mono*/im.Conv(kernel4, precision: Enums.Precision.Float); - - var images = new[] - { - conv1, - conv2, - conv3, - conv4 - }; - - using var joined = Image.Arrayjoin(images, across: 2); - joined.WriteToFile("emboss.jpg"); - - Console.WriteLine("See emboss.jpg"); - } + {1, 0, 0}, + {0, 0, 0}, + {0, 0, -1} + }, offset: 128); + using var kernel3 = kernel1.Rot270(); + using var kernel4 = kernel2.Rot90(); + + // Apply the emboss kernels + using var conv1 = /*mono*/im.Conv(kernel1, precision: Enums.Precision.Float); + using var conv2 = /*mono*/im.Conv(kernel2, precision: Enums.Precision.Float); + using var conv3 = /*mono*/im.Conv(kernel3, precision: Enums.Precision.Float); + using var conv4 = /*mono*/im.Conv(kernel4, precision: Enums.Precision.Float); + + var images = new[] + { + conv1, + conv2, + conv3, + conv4 + }; + + using var joined = Image.Arrayjoin(images, across: 2); + joined.WriteToFile("emboss.jpg"); + + Console.WriteLine("See emboss.jpg"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/GdiConvert.cs b/samples/NetVips.Samples/Samples/GdiConvert.cs index 6a360ce2..6bf6acc3 100644 --- a/samples/NetVips.Samples/Samples/GdiConvert.cs +++ b/samples/NetVips.Samples/Samples/GdiConvert.cs @@ -1,88 +1,87 @@ -namespace NetVips.Samples -{ - using System; - using Extensions; - using System.Drawing.Imaging; - using System.Runtime.Versioning; +using System; +using System.Drawing.Imaging; +using System.Runtime.Versioning; +using NetVips.Extensions; - [SupportedOSPlatform("windows")] - public class GdiConvert : ISample - { - public string Name => "GDI Convert"; - public string Category => "Utils"; +namespace NetVips.Samples; - public const string Filename = "images/PNG_transparency_demonstration_1.png"; +[SupportedOSPlatform("windows")] +public class GdiConvert : ISample +{ + public string Name => "GDI Convert"; + public string Category => "Utils"; - public void Execute(string[] args) - { - var bitmap = new System.Drawing.Bitmap(Filename); + public const string Filename = "images/PNG_transparency_demonstration_1.png"; - // 24bpp -> 32bppArgb - /*using var bitmap32Argb = new System.Drawing.Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format32bppArgb); - using (var graphics = System.Drawing.Graphics.FromImage(bitmap32Argb)) - { - graphics.DrawImage(bitmap, 0, 0, bitmap.Width, bitmap.Height); - } - using (bitmap) - { - bitmap = bitmap32Argb; - }*/ + public void Execute(string[] args) + { + var bitmap = new System.Drawing.Bitmap(Filename); - // 24bpp -> 32bppRgb - /*using var bitmap32Rgb = new System.Drawing.Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format32bppRgb); - using (var graphics = System.Drawing.Graphics.FromImage(bitmap32Rgb)) - { - graphics.DrawImage(bitmap, 0, 0, bitmap.Width, bitmap.Height); - } - using (bitmap) - { - bitmap = bitmap32Rgb; - }*/ + // 24bpp -> 32bppArgb + /*using var bitmap32Argb = new System.Drawing.Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format32bppArgb); + using (var graphics = System.Drawing.Graphics.FromImage(bitmap32Argb)) + { + graphics.DrawImage(bitmap, 0, 0, bitmap.Width, bitmap.Height); + } + using (bitmap) + { + bitmap = bitmap32Argb; + }*/ - // 24bpp -> 48bppRgb - /*using var bitmap48Rgb = new System.Drawing.Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format48bppRgb); - using (var graphics = System.Drawing.Graphics.FromImage(bitmap48Rgb)) - { - graphics.DrawImage(bitmap, 0, 0, bitmap.Width, bitmap.Height); - } - using (bitmap) - { - bitmap = bitmap48Rgb; - }*/ + // 24bpp -> 32bppRgb + /*using var bitmap32Rgb = new System.Drawing.Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format32bppRgb); + using (var graphics = System.Drawing.Graphics.FromImage(bitmap32Rgb)) + { + graphics.DrawImage(bitmap, 0, 0, bitmap.Width, bitmap.Height); + } + using (bitmap) + { + bitmap = bitmap32Rgb; + }*/ - // 24bpp -> 64bppArgb - /*using var bitmap64Argb = new System.Drawing.Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format64bppArgb); - using (var graphics = System.Drawing.Graphics.FromImage(bitmap64Argb)) - { - graphics.DrawImage(bitmap, 0, 0, bitmap.Width, bitmap.Height); - } - using (bitmap) - { - bitmap = bitmap64Argb; - }*/ + // 24bpp -> 48bppRgb + /*using var bitmap48Rgb = new System.Drawing.Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format48bppRgb); + using (var graphics = System.Drawing.Graphics.FromImage(bitmap48Rgb)) + { + graphics.DrawImage(bitmap, 0, 0, bitmap.Width, bitmap.Height); + } + using (bitmap) + { + bitmap = bitmap48Rgb; + }*/ - bitmap.Save("gdi-original.png", ImageFormat.Png); + // 24bpp -> 64bppArgb + /*using var bitmap64Argb = new System.Drawing.Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format64bppArgb); + using (var graphics = System.Drawing.Graphics.FromImage(bitmap64Argb)) + { + graphics.DrawImage(bitmap, 0, 0, bitmap.Width, bitmap.Height); + } + using (bitmap) + { + bitmap = bitmap64Argb; + }*/ - using var vipsImage = bitmap.ToVips(); - vipsImage.WriteToFile("gdi-to-vips.png"); + bitmap.Save("gdi-original.png", ImageFormat.Png); - using (bitmap) - { - bitmap = vipsImage.ToBitmap(); - } + using var vipsImage = bitmap.ToVips(); + vipsImage.WriteToFile("gdi-to-vips.png"); - bitmap.Save("vips-to-gdi.png", ImageFormat.Png); + using (bitmap) + { + bitmap = vipsImage.ToBitmap(); + } - /*using var vipsImage2 = Image.NewFromFile(Filename, access: Enums.Access.Sequential); - using (bitmap) - { - bitmap = vipsImage.ToBitmap(); - } - bitmap.Save("vips-to-gdi2.png", ImageFormat.Png); - using var image2 = bitmap.ToVips(); - image2.WriteToFile("gdi-to-vips2.png");*/ + bitmap.Save("vips-to-gdi.png", ImageFormat.Png); - Console.WriteLine("See gdi-to-vips.png"); + /*using var vipsImage2 = Image.NewFromFile(Filename, access: Enums.Access.Sequential); + using (bitmap) + { + bitmap = vipsImage.ToBitmap(); } + bitmap.Save("vips-to-gdi2.png", ImageFormat.Png); + using var image2 = bitmap.ToVips(); + image2.WriteToFile("gdi-to-vips2.png");*/ + + Console.WriteLine("See gdi-to-vips.png"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/GenerateEnums.cs b/samples/NetVips.Samples/Samples/GenerateEnums.cs index 1fc52192..a621568e 100644 --- a/samples/NetVips.Samples/Samples/GenerateEnums.cs +++ b/samples/NetVips.Samples/Samples/GenerateEnums.cs @@ -1,34 +1,34 @@ -namespace NetVips.Samples +using System; +using System.IO; +using System.Linq; +using System.Text; + +namespace NetVips.Samples; + +public class GenerateEnums : ISample { - using System; - using System.Text; - using System.Linq; - using System.IO; + public string Name => "Generate enums"; + public string Category => "Internal"; - public class GenerateEnums : ISample + private string RemovePrefix(string enumStr) { - public string Name => "Generate enums"; - public string Category => "Internal"; + const string prefix = "Vips"; - private string RemovePrefix(string enumStr) - { - const string prefix = "Vips"; - - return enumStr.StartsWith(prefix) ? enumStr[prefix.Length..] : enumStr; - } + return enumStr.StartsWith(prefix) ? enumStr[prefix.Length..] : enumStr; + } - /// - /// Generate the `Enums.Generated.cs` file. - /// - /// - /// This is used to generate the `Enums.Generated.cs` file (). - /// - /// The `Enums.Generated.cs` as string. - private string Generate() - { - var allEnums = NetVips.GetEnums(); + /// + /// Generate the `Enums.Generated.cs` file. + /// + /// + /// This is used to generate the `Enums.Generated.cs` file (). + /// + /// The `Enums.Generated.cs` as string. + private string Generate() + { + var allEnums = NetVips.GetEnums(); - const string preamble = @"//------------------------------------------------------------------------------ + const string preamble = @"//------------------------------------------------------------------------------ // // This code was generated by a tool. // libvips version: {0} @@ -38,57 +38,55 @@ private string Generate() // //------------------------------------------------------------------------------"; - var stringBuilder = - new StringBuilder(string.Format(preamble, - $"{NetVips.Version(0)}.{NetVips.Version(1)}.{NetVips.Version(2)}")); - stringBuilder.AppendLine() - .AppendLine() - .AppendLine("namespace NetVips") - .AppendLine("{") - .AppendLine(" using System;") - .AppendLine() - .AppendLine(" public partial static class Enums") - .AppendLine(" {") - .AppendLine(" #region auto-generated enums") - .AppendLine(); - - foreach (var name in allEnums) + var stringBuilder = + new StringBuilder(string.Format(preamble, + $"{NetVips.Version(0)}.{NetVips.Version(1)}.{NetVips.Version(2)}")); + stringBuilder.AppendLine() + .AppendLine() + .AppendLine("using System;") + .AppendLine() + .AppendLine("namespace NetVips;") + .AppendLine() + .AppendLine("public static class Enums") + .AppendLine("{") + .AppendLine(" #region auto-generated enums") + .AppendLine(); + + foreach (var name in allEnums) + { + var gtype = NetVips.TypeFromName(name); + var csharpName = RemovePrefix(name); + + stringBuilder.AppendLine(" /// ") + .AppendLine($" /// {csharpName}") + .AppendLine(" /// ") + .AppendLine($" public enum {csharpName}") + .AppendLine(" {"); + + var enumValues = NetVips.ValuesForEnum(gtype); + for (var i = 0; i < enumValues.Count; i++) { - var gtype = NetVips.TypeFromName(name); - var csharpName = RemovePrefix(name); - - stringBuilder.AppendLine(" /// ") - .AppendLine($" /// {csharpName}") - .AppendLine(" /// ") - .AppendLine($" public enum {csharpName}") - .AppendLine(" {"); - - var enumValues = NetVips.ValuesForEnum(gtype); - for (var i = 0; i < enumValues.Count; i++) - { - var kvp = enumValues.ElementAt(i); - var enumKey = kvp.Key.Replace('-', '_').ToPascalCase(); - - stringBuilder.AppendLine($" /// {enumKey}") - .Append($" {enumKey} = {kvp.Value}") - .AppendLine((i != enumValues.Count - 1 ? "," : string.Empty) + $" // \"{kvp.Key}\""); - } - - stringBuilder.AppendLine(" }").AppendLine(); - } + var kvp = enumValues.ElementAt(i); + var enumKey = kvp.Key.Replace('-', '_').ToPascalCase(); - stringBuilder.AppendLine(" #endregion") - .AppendLine(" }") - .AppendLine("}"); + stringBuilder.AppendLine($" /// {enumKey}") + .Append($" {enumKey} = {kvp.Value}") + .AppendLine((i != enumValues.Count - 1 ? "," : string.Empty) + $" // \"{kvp.Key}\""); + } - return stringBuilder.ToString(); + stringBuilder.AppendLine(" }").AppendLine(); } - public void Execute(string[] args) - { - File.WriteAllText("Enums.Generated.cs", Generate()); + stringBuilder.AppendLine(" #endregion") + .Append('}'); - Console.WriteLine("See Enums.Generated.cs"); - } + return stringBuilder.ToString(); + } + + public void Execute(string[] args) + { + File.WriteAllText("Enums.Generated.cs", Generate()); + + Console.WriteLine("See Enums.Generated.cs"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/GenerateImageClass.cs b/samples/NetVips.Samples/Samples/GenerateImageClass.cs index 26d10c8e..0e6991ef 100644 --- a/samples/NetVips.Samples/Samples/GenerateImageClass.cs +++ b/samples/NetVips.Samples/Samples/GenerateImageClass.cs @@ -1,249 +1,563 @@ -namespace NetVips.Samples +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace NetVips.Samples; + +// TODO(kleisauke): This class should probably be refactored, although it does its job. +public class GenerateImageClass : ISample { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.IO; - - // TODO(kleisauke): This class should probably be refactored, although it does its job. - public class GenerateImageClass : ISample + public string Name => "Generate image class"; + public string Category => "Internal"; + + private readonly Dictionary _gTypeToCSharpDict = new() + { + {GValue.GBoolType, "bool"}, + {GValue.GIntType, "int"}, + {GValue.GUint64Type, "ulong"}, + //{GValue.GEnumType, "string"}, // Checked below + //{GValue.GFlagsType, "int"}, // Checked below + {GValue.GDoubleType, "double"}, + {GValue.GStrType, "string"}, + {GValue.GObjectType, "GObject"}, + {GValue.ImageType, "Image"}, + {GValue.ArrayIntType, "int[]"}, + {GValue.ArrayDoubleType, "double[]"}, + {GValue.ArrayImageType, "Image[]"}, + {GValue.RefStrType, "string"}, + {GValue.BlobType, "byte[]"} + }; + + private readonly List _allNickNames = NetVips.GetOperations(); + + /// + /// The fundamental type for VipsFailOn. See . + /// + public static readonly nint FailOnType = NetVips.TypeFromName("VipsFailOn"); + + /// + /// The fundamental type for VipsForeignKeep. See . + /// + public static readonly nint ForeignKeepType = NetVips.TypeFromName("VipsForeignKeep"); + + public GenerateImageClass() + { + // Classes + _gTypeToCSharpDict.Add(GValue.SourceType, "Source"); + _gTypeToCSharpDict.Add(GValue.TargetType, "Target"); + + // Enums + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsAccess"), "Enums.Access"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsAlign"), "Enums.Align"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsAngle"), "Enums.Angle"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsAngle45"), "Enums.Angle45"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsBandFormat"), "Enums.BandFormat"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsBlendMode"), "Enums.BlendMode"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsCoding"), "Enums.Coding"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsCombine"), "Enums.Combine"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsCombineMode"), "Enums.CombineMode"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsCompassDirection"), "Enums.CompassDirection"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsDemandStyle"), "Enums.DemandStyle"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsDirection"), "Enums.Direction"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsExtend"), "Enums.Extend"); + if (FailOnType != IntPtr.Zero) + { + _gTypeToCSharpDict.Add(FailOnType, "Enums.FailOn"); + } + + if (NetVips.TypeFind("VipsOperation", "dzsave") != IntPtr.Zero) + { + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignDzContainer"), "Enums.ForeignDzContainer"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignDzLayout"), "Enums.ForeignDzLayout"); + } + + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignDzDepth"), "Enums.ForeignDzDepth"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignHeifCompression"), "Enums.ForeignHeifCompression"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignPpmFormat"), "Enums.ForeignPpmFormat"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignSubsample"), "Enums.ForeignSubsample"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignTiffCompression"), "Enums.ForeignTiffCompression"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignTiffPredictor"), "Enums.ForeignTiffPredictor"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignTiffResunit"), "Enums.ForeignTiffResunit"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignWebpPreset"), "Enums.ForeignWebpPreset"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsIntent"), "Enums.Intent"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsInteresting"), "Enums.Interesting"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsInterpretation"), "Enums.Interpretation"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsKernel"), "Enums.Kernel"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsOperationBoolean"), "Enums.OperationBoolean"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsOperationComplex"), "Enums.OperationComplex"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsOperationComplex2"), "Enums.OperationComplex2"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsOperationComplexget"), "Enums.OperationComplexget"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsOperationMath"), "Enums.OperationMath"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsOperationMath2"), "Enums.OperationMath2"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsOperationMorphology"), "Enums.OperationMorphology"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsOperationRelational"), "Enums.OperationRelational"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsOperationRound"), "Enums.OperationRound"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsPCS"), "Enums.PCS"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsPrecision"), "Enums.Precision"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsRegionShrink"), "Enums.RegionShrink"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsSize"), "Enums.Size"); + if (NetVips.AtLeastLibvips(8, 14)) + { + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsTextWrap"), "Enums.TextWrap"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignHeifEncoder"), "Enums.ForeignHeifEncoder"); + } + + // Flags + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignFlags"), "Enums.ForeignFlags"); + _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignPngFilter"), "Enums.ForeignPngFilter"); + if (ForeignKeepType != IntPtr.Zero) + { + _gTypeToCSharpDict.Add(ForeignKeepType, "Enums.ForeignKeep"); + } + } + + /// + /// Map a GType to the name of the C# type we use to represent it. + /// + /// The GType identifier. + /// The GType to map. + /// The C# type we use to represent it. + private string GTypeToCSharp(string name, nint gtype) + { + if (_gTypeToCSharpDict.ContainsKey(gtype)) + { + return _gTypeToCSharpDict[gtype]; + } + + var fundamental = NetVips.FundamentalType(gtype); + + if (_gTypeToCSharpDict.ContainsKey(fundamental)) + { + return _gTypeToCSharpDict[fundamental]; + } + + throw new Exception($"Unsupported type: {gtype} name: {name}"); + } + + /// + /// Make a C#-style docstring + function declaration. + /// + /// + /// This is used to generate the functions in . + /// + /// Operation name. + /// Indentation level. + /// Generate . + /// The out parameters of this function. + /// A C#-style docstring + function declaration. + private string GenerateFunction(string operationName, string indent = " ", + bool mutable = false, IReadOnlyList outParameters = null) { - public string Name => "Generate image class"; - public string Category => "Internal"; - - private readonly Dictionary _gTypeToCSharpDict = new() - { - {GValue.GBoolType, "bool"}, - {GValue.GIntType, "int"}, - {GValue.GUint64Type, "ulong"}, - //{GValue.GEnumType, "string"}, // Checked below - //{GValue.GFlagsType, "int"}, // Checked below - {GValue.GDoubleType, "double"}, - {GValue.GStrType, "string"}, - {GValue.GObjectType, "GObject"}, - {GValue.ImageType, "Image"}, - {GValue.ArrayIntType, "int[]"}, - {GValue.ArrayDoubleType, "double[]"}, - {GValue.ArrayImageType, "Image[]"}, - {GValue.RefStrType, "string"}, - {GValue.BlobType, "byte[]"} + using var op = Operation.NewFromName(operationName); + if ((op.GetFlags() & Enums.OperationFlags.DEPRECATED) != 0) + { + throw new ArgumentException($"No such operator. Operator \"{operationName}\" is deprecated"); + } + + var intro = Introspect.Get(operationName); + + // we are only interested in non-deprecated args + var optionalInput = intro.OptionalInput + .Where(arg => (arg.Value.Flags & Enums.ArgumentFlags.DEPRECATED) == 0) + .Select(x => x.Value) + .ToArray(); + var optionalOutput = intro.OptionalOutput + .Where(arg => (arg.Value.Flags & Enums.ArgumentFlags.DEPRECATED) == 0) + .Select(x => x.Value) + .ToArray(); + + // these are always non-deprecated + var requiredInput = intro.RequiredInput.ToArray(); + var requiredOutput = intro.RequiredOutput.ToArray(); + + if (mutable ^ intro.Mutable) + { + throw new ArgumentException( + $"Cannot generate \"{operationName}\" as this is a {(intro.Mutable ? string.Empty : "non-")}mutable operation."); + } + + string[] reservedKeywords = + { + "in", "ref", "out", "ushort" }; - private readonly List _allNickNames = NetVips.GetOperations(); + string SafeIdentifier(string name) => + reservedKeywords.Contains(name) + ? $"@{name}" + : name; + + string SafeCast(string type, string name = "result") + { + return type switch + { + "GObject" => $" as {type};", + "Image" => $" as {type};", + "int[]" => $" as {type};", + "double[]" => $" as {type};", + "byte[]" => $" as {type};", + "Image[]" => $" as {type};", + "object[]" => $" as {type};", + "bool" => $" is {type} {name} && {name};", + "int" => $" is {type} {name} ? {name} : 0;", + "ulong" => $" is {type} {name} ? {name} : 0ul;", + "double" => $" is {type} {name} ? {name} : 0d;", + "string" => $" is {type} {name} ? {name} : null;", + _ => ";" + }; + } + + string ExplicitCast(string type) + { + return type switch + { + { } enumString when enumString.StartsWith("Enums.") => $"({type})", + _ => string.Empty + }; + } + + string ToNullable(string type, string name) + { + return type switch + { + "Image[]" => $"{type} {name} = null", + "object[]" => $"{type} {name} = null", + "int[]" => $"{type} {name} = null", + "double[]" => $"{type} {name} = null", + "byte[]" => $"{type} {name} = null", + "GObject" => $"{type} {name} = null", + "Image" => $"{type} {name} = null", + "string" => $"{type} {name} = null", + "bool" => $"{type}? {name} = null", + "int" => $"{type}? {name} = null", + "ulong" => $"{type}? {name} = null", + "double" => $"{type}? {name} = null", + { } enumString when enumString.StartsWith("Enums.") => $"{type}? {name} = null", + _ => throw new Exception($"Unsupported type: {type}") + }; + } - /// - /// The fundamental type for VipsFailOn. See . - /// - public static readonly nint FailOnType = NetVips.TypeFromName("VipsFailOn"); + var result = new StringBuilder($"{indent}/// \n"); - /// - /// The fundamental type for VipsForeignKeep. See . - /// - public static readonly nint ForeignKeepType = NetVips.TypeFromName("VipsForeignKeep"); + var newOperationName = operationName.ToPascalCase(); + + var description = op.GetDescription(); + result.AppendLine($"{indent}/// {description.FirstLetterToUpper()}.") + .AppendLine($"{indent}/// ") + .AppendLine($"{indent}/// ") + .AppendLine($"{indent}/// ") + .Append($"{indent}/// "); + + if (requiredOutput.Length == 1) + { + var arg = requiredOutput.First(); + result.Append( + $"{(arg.Type == GValue.ImageType ? "using " : string.Empty)}{GTypeToCSharp(arg.Name, arg.Type)} {SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()} = "); + } + else if (requiredOutput.Length > 1) + { + result.Append("var output = "); + } + else if (intro.Mutable && intro.MemberX.HasValue) + { + result.Append("image.Mutate(x => "); + } - public GenerateImageClass() + if (intro.MemberX.HasValue) { - // Classes - _gTypeToCSharpDict.Add(GValue.SourceType, "Source"); - _gTypeToCSharpDict.Add(GValue.TargetType, "Target"); + result.Append(intro.Mutable ? "x" : intro.MemberX.Value.Name); + } + else + { + result.Append("NetVips.Image"); + } - // Enums - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsAccess"), "Enums.Access"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsAlign"), "Enums.Align"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsAngle"), "Enums.Angle"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsAngle45"), "Enums.Angle45"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsBandFormat"), "Enums.BandFormat"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsBlendMode"), "Enums.BlendMode"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsCoding"), "Enums.Coding"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsCombine"), "Enums.Combine"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsCombineMode"), "Enums.CombineMode"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsCompassDirection"), "Enums.CompassDirection"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsDemandStyle"), "Enums.DemandStyle"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsDirection"), "Enums.Direction"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsExtend"), "Enums.Extend"); - if (FailOnType != IntPtr.Zero) + result.Append( + $".{newOperationName}({string.Join(", ", requiredInput.Select(arg => SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()).ToArray())}"); + + if (outParameters != null) + { + if (requiredInput.Length > 0) { - _gTypeToCSharpDict.Add(FailOnType, "Enums.FailOn"); + result.Append(", "); } - if (NetVips.TypeFind("VipsOperation", "dzsave") != IntPtr.Zero) + result.Append( + $"{string.Join(", ", outParameters.Select(arg => $"out var {SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()}").ToArray())}"); + } + + if (optionalInput.Length > 0) + { + if (requiredInput.Length > 0 || outParameters != null) { - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignDzContainer"), "Enums.ForeignDzContainer"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignDzLayout"), "Enums.ForeignDzLayout"); + result.Append(", "); } - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignDzDepth"), "Enums.ForeignDzDepth"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignHeifCompression"), "Enums.ForeignHeifCompression"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignPpmFormat"), "Enums.ForeignPpmFormat"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignSubsample"), "Enums.ForeignSubsample"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignTiffCompression"), "Enums.ForeignTiffCompression"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignTiffPredictor"), "Enums.ForeignTiffPredictor"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignTiffResunit"), "Enums.ForeignTiffResunit"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignWebpPreset"), "Enums.ForeignWebpPreset"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsIntent"), "Enums.Intent"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsInteresting"), "Enums.Interesting"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsInterpretation"), "Enums.Interpretation"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsKernel"), "Enums.Kernel"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsOperationBoolean"), "Enums.OperationBoolean"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsOperationComplex"), "Enums.OperationComplex"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsOperationComplex2"), "Enums.OperationComplex2"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsOperationComplexget"), "Enums.OperationComplexget"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsOperationMath"), "Enums.OperationMath"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsOperationMath2"), "Enums.OperationMath2"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsOperationMorphology"), "Enums.OperationMorphology"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsOperationRelational"), "Enums.OperationRelational"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsOperationRound"), "Enums.OperationRound"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsPCS"), "Enums.PCS"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsPrecision"), "Enums.Precision"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsRegionShrink"), "Enums.RegionShrink"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsSize"), "Enums.Size"); - if (NetVips.AtLeastLibvips(8, 14)) + for (var i = 0; i < optionalInput.Length; i++) { - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsTextWrap"), "Enums.TextWrap"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignHeifEncoder"), "Enums.ForeignHeifEncoder"); + var arg = optionalInput[i]; + result.Append( + $"{SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()}: {GTypeToCSharp(arg.Name, arg.Type)}") + .Append(i != optionalInput.Length - 1 ? ", " : string.Empty); } + } + + result.Append(intro.Mutable ? ")" : string.Empty) + .AppendLine(");"); + + result.AppendLine($"{indent}/// ") + .AppendLine($"{indent}/// "); + + foreach (var arg in requiredInput) + { + result.AppendLine( + $"{indent}/// {op.GetBlurb(arg.Name)}."); + } - // Flags - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignFlags"), "Enums.ForeignFlags"); - _gTypeToCSharpDict.Add(NetVips.TypeFromName("VipsForeignPngFilter"), "Enums.ForeignPngFilter"); - if (ForeignKeepType != IntPtr.Zero) + if (outParameters != null) + { + foreach (var outParameter in outParameters) { - _gTypeToCSharpDict.Add(ForeignKeepType, "Enums.ForeignKeep"); + result.AppendLine( + $"{indent}/// {op.GetBlurb(outParameter.Name)}."); } } - /// - /// Map a GType to the name of the C# type we use to represent it. - /// - /// The GType identifier. - /// The GType to map. - /// The C# type we use to represent it. - private string GTypeToCSharp(string name, nint gtype) + foreach (var arg in optionalInput) + { + result.AppendLine( + $"{indent}/// {op.GetBlurb(arg.Name)}."); + } + + string outputType; + + var outputTypes = requiredOutput.Select(arg => GTypeToCSharp(arg.Name, arg.Type)).ToArray(); + outputType = outputTypes.Length switch + { + 0 => "void", + 1 => outputTypes[0], + _ => outputTypes.Any(o => o != outputTypes[0]) ? $"{outputTypes[0]}[]" : "object[]" + }; + + string ToCref(string name) => + name.Equals("Image") || name.Equals("GObject") ? $"new " : name; + + if (outputType.EndsWith("[]")) + { + result.AppendLine( + $"{indent}/// An array of {ToCref(outputType.Remove(outputType.Length - 2))}s."); + } + else if (!outputType.Equals("void")) + { + result.AppendLine($"{indent}/// A {ToCref(outputType)}."); + } + + result.Append($"{indent}public ") + .Append(intro.MemberX.HasValue ? string.Empty : "static ") + .Append(outputType); + + if (requiredInput.Length == 1 && outParameters == null && optionalInput.Length == 0 && + requiredInput[0].Type == GValue.ArrayImageType) { - if (_gTypeToCSharpDict.ContainsKey(gtype)) + // We could safely use the params keyword + result.Append( + $" {newOperationName}(params Image[] {SafeIdentifier(requiredInput[0].Name).ToPascalCase().FirstLetterToLower()}"); + } + else + { + result.Append( + $" {newOperationName}(" + + string.Join(", ", + requiredInput.Select(arg => + $"{GTypeToCSharp(arg.Name, arg.Type)} {SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()}") + .ToArray())); + } + + if (outParameters != null) + { + if (requiredInput.Length > 0) { - return _gTypeToCSharpDict[gtype]; + result.Append(", "); } - var fundamental = NetVips.FundamentalType(gtype); + result.Append(string.Join(", ", + outParameters.Select(arg => + $"out {GTypeToCSharp(arg.Name, arg.Type)} {SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()}") + .ToArray())); + } - if (_gTypeToCSharpDict.ContainsKey(fundamental)) + if (optionalInput.Length > 0) + { + if (requiredInput.Length > 0 || outParameters != null) { - return _gTypeToCSharpDict[fundamental]; + result.Append(", "); } - throw new Exception($"Unsupported type: {gtype} name: {name}"); + result.Append(string.Join(", ", + optionalInput.Select(arg => + $"{ToNullable(GTypeToCSharp(arg.Name, arg.Type), SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower())}") + .ToArray())); } - /// - /// Make a C#-style docstring + function declaration. - /// - /// - /// This is used to generate the functions in . - /// - /// Operation name. - /// Indentation level. - /// Generate . - /// The out parameters of this function. - /// A C#-style docstring + function declaration. - private string GenerateFunction(string operationName, string indent = " ", - bool mutable = false, IReadOnlyList outParameters = null) + result.AppendLine(")") + .AppendLine($"{indent}{{"); + + if (optionalInput.Length > 0) { - using var op = Operation.NewFromName(operationName); - if ((op.GetFlags() & Enums.OperationFlags.DEPRECATED) != 0) + result.AppendLine($"{indent} var options = new VOption();").AppendLine(); + + foreach (var arg in optionalInput) { - throw new ArgumentException($"No such operator. Operator \"{operationName}\" is deprecated"); + var safeIdentifier = SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower(); + var optionsName = safeIdentifier == arg.Name + ? $"nameof({arg.Name})" + : $"\"{arg.Name}\""; + + if (operationName.StartsWith("dzsave") && arg.Name == "imagename") + { + result.AppendLine( + $"{indent} options.AddIfPresent(NetVips.AtLeastLibvips(8, 15) ? {optionsName} : \"basename\", {safeIdentifier});"); + } + else if (arg.Type == FailOnType) + { + result.AppendLine($"{indent} options.AddFailOn({safeIdentifier});"); + } + else if (arg.Type == ForeignKeepType) + { + result.Append($"{indent} options.AddForeignKeep({safeIdentifier}"); + result.AppendLine(operationName.StartsWith("dzsave") ? ", true);" : ");"); + } + else + { + result.AppendLine($"{indent} options.AddIfPresent({optionsName}, {safeIdentifier});"); + } } - var intro = Introspect.Get(operationName); + result.AppendLine(); + } - // we are only interested in non-deprecated args - var optionalInput = intro.OptionalInput - .Where(arg => (arg.Value.Flags & Enums.ArgumentFlags.DEPRECATED) == 0) - .Select(x => x.Value) - .ToArray(); - var optionalOutput = intro.OptionalOutput - .Where(arg => (arg.Value.Flags & Enums.ArgumentFlags.DEPRECATED) == 0) - .Select(x => x.Value) - .ToArray(); + if (outParameters != null) + { + if (optionalInput.Length > 0) + { + foreach (var arg in outParameters) + { + result.AppendLine($"{indent} options.Add(\"{arg.Name}\", true);"); + } + } + else + { + result.AppendLine($"{indent} var optionalOutput = new VOption") + .AppendLine($"{indent} {{"); + for (var i = 0; i < outParameters.Count; i++) + { + var arg = outParameters[i]; + result.Append($"{indent} {{\"{arg.Name}\", true}}") + .AppendLine(i != outParameters.Count - 1 ? "," : string.Empty); + } - // these are always non-deprecated - var requiredInput = intro.RequiredInput.ToArray(); - var requiredOutput = intro.RequiredOutput.ToArray(); + result.AppendLine($"{indent} }};"); + } - if (mutable ^ intro.Mutable) + result.AppendLine() + .Append($"{indent} var results = ") + .Append(intro.MemberX.HasValue ? "this" : "Operation") + .Append($".Call(\"{operationName}\"") + .Append(optionalInput.Length > 0 ? ", options" : ", optionalOutput"); + } + else + { + result.Append($"{indent} "); + if (outputType != "void") { - throw new ArgumentException( - $"Cannot generate \"{operationName}\" as this is a {(intro.Mutable ? string.Empty : "non-")}mutable operation."); + result.Append("return "); } - string[] reservedKeywords = + result.Append(intro.MemberX.HasValue ? "this" : "Operation") + .Append($".Call(\"{operationName}\""); + if (optionalInput.Length > 0) { - "in", "ref", "out", "ushort" - }; + result.Append(", options"); + } + } - string SafeIdentifier(string name) => - reservedKeywords.Contains(name) - ? $"@{name}" - : name; + if (requiredInput.Length > 0) + { + result.Append(", "); - string SafeCast(string type, string name = "result") + // Co-variant array conversion from Image[] to object[] can cause run-time exception on write operation. + // So we need to wrap the image array into a object array. + var needToWrap = requiredInput.Length == 1 && + GTypeToCSharp(requiredInput[0].Name, requiredInput[0].Type).Equals("Image[]"); + if (needToWrap) { - return type switch - { - "GObject" => $" as {type};", - "Image" => $" as {type};", - "int[]" => $" as {type};", - "double[]" => $" as {type};", - "byte[]" => $" as {type};", - "Image[]" => $" as {type};", - "object[]" => $" as {type};", - "bool" => $" is {type} {name} && {name};", - "int" => $" is {type} {name} ? {name} : 0;", - "ulong" => $" is {type} {name} ? {name} : 0ul;", - "double" => $" is {type} {name} ? {name} : 0d;", - "string" => $" is {type} {name} ? {name} : null;", - _ => ";" - }; - } - - string ExplicitCast(string type) - { - return type switch - { - { } enumString when enumString.StartsWith("Enums.") => $"({type})", - _ => string.Empty - }; + result.Append("new object[] { "); } - string ToNullable(string type, string name) + result.Append(string.Join(", ", + requiredInput.Select(arg => SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()) + .ToArray())); + + if (needToWrap) { - return type switch - { - "Image[]" => $"{type} {name} = null", - "object[]" => $"{type} {name} = null", - "int[]" => $"{type} {name} = null", - "double[]" => $"{type} {name} = null", - "byte[]" => $"{type} {name} = null", - "GObject" => $"{type} {name} = null", - "Image" => $"{type} {name} = null", - "string" => $"{type} {name} = null", - "bool" => $"{type}? {name} = null", - "int" => $"{type}? {name} = null", - "ulong" => $"{type}? {name} = null", - "double" => $"{type}? {name} = null", - { } enumString when enumString.StartsWith("Enums.") => $"{type}? {name} = null", - _ => throw new Exception($"Unsupported type: {type}") - }; - } - - var result = new StringBuilder($"{indent}/// \n"); - - var newOperationName = operationName.ToPascalCase(); - - var description = op.GetDescription(); - result.AppendLine($"{indent}/// {description.FirstLetterToUpper()}.") + result.Append(" }"); + } + } + + result.Append(')'); + + if (outParameters != null) + { + result.AppendLine(" as object[];"); + if (outputType != "void") + { + result.Append($"{indent} var finalResult = results?[0]") + .Append(SafeCast(outputType)); + } + } + else + { + result.Append(SafeCast(outputType)) + .AppendLine(); + } + + if (outParameters != null) + { + result.AppendLine() + .AppendLine($"{indent} var opts = results?[1] as VOption;"); + for (var i = 0; i < outParameters.Count; i++) + { + var arg = outParameters[i]; + result.Append( + $"{indent} {SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()} = {ExplicitCast(GTypeToCSharp(arg.Name, arg.Type))}opts?[\"{arg.Name}\"]") + .AppendLine(SafeCast(GTypeToCSharp(arg.Name, arg.Type), $"out{i + 1}")); + } + + if (outputType != "void") + { + result.AppendLine() + .Append($"{indent} return finalResult;") + .AppendLine(); + } + } + + result.Append($"{indent}}}") + .AppendLine(); + + nint firstArgType = requiredInput.Length > 0 ? op.GetTypeOf(requiredInput[0].Name) : IntPtr.Zero; + + // Create stream overload if necessary + if (firstArgType == GValue.SourceType || firstArgType == GValue.TargetType) + { + var replace = firstArgType == GValue.SourceType ? "source" : "target"; + requiredInput = requiredInput.Skip(1).ToArray(); + var oldOperationName = newOperationName; + newOperationName = newOperationName.Replace(replace.FirstLetterToUpper(), "Stream"); + + result.AppendLine() + .AppendLine($"{indent}/// ") + .AppendLine($"{indent}/// {description.Replace(replace, "stream").FirstLetterToUpper()}.") .AppendLine($"{indent}/// ") .AppendLine($"{indent}/// ") .AppendLine($"{indent}/// ") @@ -259,22 +573,10 @@ string ToNullable(string type, string name) { result.Append("var output = "); } - else if (intro.Mutable && intro.MemberX.HasValue) - { - result.Append("image.Mutate(x => "); - } - if (intro.MemberX.HasValue) - { - result.Append(intro.Mutable ? "x" : intro.MemberX.Value.Name); - } - else - { - result.Append("NetVips.Image"); - } - - result.Append( - $".{newOperationName}({string.Join(", ", requiredInput.Select(arg => SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()).ToArray())}"); + result.Append(intro.MemberX.HasValue ? intro.MemberX.Value.Name : "NetVips.Image") + .Append( + $".{newOperationName}(stream, {string.Join(", ", requiredInput.Select(arg => SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()).ToArray())}"); if (outParameters != null) { @@ -303,17 +605,13 @@ string ToNullable(string type, string name) } } - result.Append(intro.Mutable ? ")" : string.Empty) - .AppendLine(");"); + result.AppendLine(");"); result.AppendLine($"{indent}/// ") .AppendLine($"{indent}/// "); - foreach (var arg in requiredInput) - { - result.AppendLine( - $"{indent}/// {op.GetBlurb(arg.Name)}."); - } + result.AppendLine( + $"{indent}/// Stream to {(firstArgType == GValue.SourceType ? "load from" : "save to")}."); if (outParameters != null) { @@ -324,15 +622,18 @@ string ToNullable(string type, string name) } } - foreach (var arg in optionalInput) + foreach (var arg in requiredInput) { result.AppendLine( $"{indent}/// {op.GetBlurb(arg.Name)}."); } - string outputType; + foreach (var arg in optionalInput) + { + result.AppendLine( + $"{indent}/// {op.GetBlurb(arg.Name)}."); + } - var outputTypes = requiredOutput.Select(arg => GTypeToCSharp(arg.Name, arg.Type)).ToArray(); outputType = outputTypes.Length switch { 0 => "void", @@ -340,15 +641,7 @@ string ToNullable(string type, string name) _ => outputTypes.Any(o => o != outputTypes[0]) ? $"{outputTypes[0]}[]" : "object[]" }; - string ToCref(string name) => - name.Equals("Image") || name.Equals("GObject") ? $"new " : name; - - if (outputType.EndsWith("[]")) - { - result.AppendLine( - $"{indent}/// An array of {ToCref(outputType.Remove(outputType.Length - 2))}s."); - } - else if (!outputType.Equals("void")) + if (!outputType.Equals("void")) { result.AppendLine($"{indent}/// A {ToCref(outputType)}."); } @@ -357,22 +650,12 @@ string ToCref(string name) => .Append(intro.MemberX.HasValue ? string.Empty : "static ") .Append(outputType); - if (requiredInput.Length == 1 && outParameters == null && optionalInput.Length == 0 && - requiredInput[0].Type == GValue.ArrayImageType) - { - // We could safely use the params keyword - result.Append( - $" {newOperationName}(params Image[] {SafeIdentifier(requiredInput[0].Name).ToPascalCase().FirstLetterToLower()}"); - } - else - { - result.Append( - $" {newOperationName}(" + - string.Join(", ", - requiredInput.Select(arg => - $"{GTypeToCSharp(arg.Name, arg.Type)} {SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()}") - .ToArray())); - } + result.Append( + $" {newOperationName}(Stream stream, " + + string.Join(", ", + requiredInput.Select(arg => + $"{GTypeToCSharp(arg.Name, arg.Type)} {SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()}") + .ToArray())); if (outParameters != null) { @@ -401,383 +684,100 @@ string ToCref(string name) => } result.AppendLine(")") - .AppendLine($"{indent}{{"); - - if (optionalInput.Length > 0) - { - result.AppendLine($"{indent} var options = new VOption();").AppendLine(); - - foreach (var arg in optionalInput) - { - var safeIdentifier = SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower(); - var optionsName = safeIdentifier == arg.Name - ? $"nameof({arg.Name})" - : $"\"{arg.Name}\""; - - if (operationName.StartsWith("dzsave") && arg.Name == "imagename") - { - result.AppendLine( - $"{indent} options.AddIfPresent(NetVips.AtLeastLibvips(8, 15) ? {optionsName} : \"basename\", {safeIdentifier});"); - } - else if (arg.Type == FailOnType) - { - result.AppendLine($"{indent} options.AddFailOn({safeIdentifier});"); - } - else if (arg.Type == ForeignKeepType) - { - result.Append($"{indent} options.AddForeignKeep({safeIdentifier}"); - result.AppendLine(operationName.StartsWith("dzsave") ? ", true);" : ");"); - } - else - { - result.AppendLine($"{indent} options.AddIfPresent({optionsName}, {safeIdentifier});"); - } - } - - result.AppendLine(); - } + .AppendLine($"{indent}{{") + .AppendLine(firstArgType == GValue.SourceType + ? $"{indent} var source = SourceStream.NewFromStream(stream);" + : $"{indent} using var target = TargetStream.NewFromStream(stream);") + .Append($"{indent} ") + .Append(firstArgType == GValue.SourceType ? "var image = " : string.Empty) + .Append($"{oldOperationName}(") + .Append(firstArgType == GValue.SourceType ? "source" : "target") + .Append(", ") + .Append(string.Join(", ", + requiredInput.Select(arg => $"{SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()}") + .ToArray())); if (outParameters != null) { - if (optionalInput.Length > 0) - { - foreach (var arg in outParameters) - { - result.AppendLine($"{indent} options.Add(\"{arg.Name}\", true);"); - } - } - else - { - result.AppendLine($"{indent} var optionalOutput = new VOption") - .AppendLine($"{indent} {{"); - for (var i = 0; i < outParameters.Count; i++) - { - var arg = outParameters[i]; - result.Append($"{indent} {{\"{arg.Name}\", true}}") - .AppendLine(i != outParameters.Count - 1 ? "," : string.Empty); - } - - result.AppendLine($"{indent} }};"); - } - - result.AppendLine() - .Append($"{indent} var results = ") - .Append(intro.MemberX.HasValue ? "this" : "Operation") - .Append($".Call(\"{operationName}\"") - .Append(optionalInput.Length > 0 ? ", options" : ", optionalOutput"); - } - else - { - result.Append($"{indent} "); - if (outputType != "void") - { - result.Append("return "); - } - - result.Append(intro.MemberX.HasValue ? "this" : "Operation") - .Append($".Call(\"{operationName}\""); - if (optionalInput.Length > 0) - { - result.Append(", options"); - } - } - - if (requiredInput.Length > 0) - { - result.Append(", "); - - // Co-variant array conversion from Image[] to object[] can cause run-time exception on write operation. - // So we need to wrap the image array into a object array. - var needToWrap = requiredInput.Length == 1 && - GTypeToCSharp(requiredInput[0].Name, requiredInput[0].Type).Equals("Image[]"); - if (needToWrap) + if (requiredInput.Length > 0) { - result.Append("new object[] { "); + result.Append(", "); } result.Append(string.Join(", ", - requiredInput.Select(arg => SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()) + outParameters.Select(arg => + $"out {SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()}") .ToArray())); - - if (needToWrap) - { - result.Append(" }"); - } - } - - result.Append(')'); - - if (outParameters != null) - { - result.AppendLine(" as object[];"); - if (outputType != "void") - { - result.Append($"{indent} var finalResult = results?[0]") - .Append(SafeCast(outputType)); - } - } - else - { - result.Append(SafeCast(outputType)) - .AppendLine(); } - if (outParameters != null) + if (optionalInput.Length > 0) { - result.AppendLine() - .AppendLine($"{indent} var opts = results?[1] as VOption;"); - for (var i = 0; i < outParameters.Count; i++) + if (requiredInput.Length > 0 || outParameters != null) { - var arg = outParameters[i]; - result.Append( - $"{indent} {SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()} = {ExplicitCast(GTypeToCSharp(arg.Name, arg.Type))}opts?[\"{arg.Name}\"]") - .AppendLine(SafeCast(GTypeToCSharp(arg.Name, arg.Type), $"out{i + 1}")); + result.Append(", "); } - if (outputType != "void") - { - result.AppendLine() - .Append($"{indent} return finalResult;") - .AppendLine(); - } + result.Append(string.Join(", ", + optionalInput.Select(arg => + $"{SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()}") + .ToArray())); } - result.Append($"{indent}}}") - .AppendLine(); - - nint firstArgType = requiredInput.Length > 0 ? op.GetTypeOf(requiredInput[0].Name) : IntPtr.Zero; + result.AppendLine(");"); - // Create stream overload if necessary - if (firstArgType == GValue.SourceType || firstArgType == GValue.TargetType) + if (firstArgType == GValue.SourceType) { - var replace = firstArgType == GValue.SourceType ? "source" : "target"; - requiredInput = requiredInput.Skip(1).ToArray(); - var oldOperationName = newOperationName; - newOperationName = newOperationName.Replace(replace.FirstLetterToUpper(), "Stream"); - result.AppendLine() - .AppendLine($"{indent}/// ") - .AppendLine($"{indent}/// {description.Replace(replace, "stream").FirstLetterToUpper()}.") - .AppendLine($"{indent}/// ") - .AppendLine($"{indent}/// ") - .AppendLine($"{indent}/// ") - .Append($"{indent}/// "); - - if (requiredOutput.Length == 1) - { - var arg = requiredOutput.First(); - result.Append( - $"{(arg.Type == GValue.ImageType ? "using " : string.Empty)}{GTypeToCSharp(arg.Name, arg.Type)} {SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()} = "); - } - else if (requiredOutput.Length > 1) - { - result.Append("var output = "); - } - - result.Append(intro.MemberX.HasValue ? intro.MemberX.Value.Name : "NetVips.Image") - .Append( - $".{newOperationName}(stream, {string.Join(", ", requiredInput.Select(arg => SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()).ToArray())}"); - - if (outParameters != null) - { - if (requiredInput.Length > 0) - { - result.Append(", "); - } - - result.Append( - $"{string.Join(", ", outParameters.Select(arg => $"out var {SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()}").ToArray())}"); - } - - if (optionalInput.Length > 0) - { - if (requiredInput.Length > 0 || outParameters != null) - { - result.Append(", "); - } - - for (var i = 0; i < optionalInput.Length; i++) - { - var arg = optionalInput[i]; - result.Append( - $"{SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()}: {GTypeToCSharp(arg.Name, arg.Type)}") - .Append(i != optionalInput.Length - 1 ? ", " : string.Empty); - } - } - - result.AppendLine(");"); - - result.AppendLine($"{indent}/// ") - .AppendLine($"{indent}/// "); - - result.AppendLine( - $"{indent}/// Stream to {(firstArgType == GValue.SourceType ? "load from" : "save to")}."); - - if (outParameters != null) - { - foreach (var outParameter in outParameters) - { - result.AppendLine( - $"{indent}/// {op.GetBlurb(outParameter.Name)}."); - } - } - - foreach (var arg in requiredInput) - { - result.AppendLine( - $"{indent}/// {op.GetBlurb(arg.Name)}."); - } - - foreach (var arg in optionalInput) - { - result.AppendLine( - $"{indent}/// {op.GetBlurb(arg.Name)}."); - } - - outputType = outputTypes.Length switch - { - 0 => "void", - 1 => outputTypes[0], - _ => outputTypes.Any(o => o != outputTypes[0]) ? $"{outputTypes[0]}[]" : "object[]" - }; - - if (!outputType.Equals("void")) - { - result.AppendLine($"{indent}/// A {ToCref(outputType)}."); - } - - result.Append($"{indent}public ") - .Append(intro.MemberX.HasValue ? string.Empty : "static ") - .Append(outputType); - - result.Append( - $" {newOperationName}(Stream stream, " + - string.Join(", ", - requiredInput.Select(arg => - $"{GTypeToCSharp(arg.Name, arg.Type)} {SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()}") - .ToArray())); - - if (outParameters != null) - { - if (requiredInput.Length > 0) - { - result.Append(", "); - } - - result.Append(string.Join(", ", - outParameters.Select(arg => - $"out {GTypeToCSharp(arg.Name, arg.Type)} {SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()}") - .ToArray())); - } - - if (optionalInput.Length > 0) - { - if (requiredInput.Length > 0 || outParameters != null) - { - result.Append(", "); - } - - result.Append(string.Join(", ", - optionalInput.Select(arg => - $"{ToNullable(GTypeToCSharp(arg.Name, arg.Type), SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower())}") - .ToArray())); - } - - result.AppendLine(")") - .AppendLine($"{indent}{{") - .AppendLine(firstArgType == GValue.SourceType - ? $"{indent} var source = SourceStream.NewFromStream(stream);" - : $"{indent} using var target = TargetStream.NewFromStream(stream);") - .Append($"{indent} ") - .Append(firstArgType == GValue.SourceType ? "var image = " : string.Empty) - .Append($"{oldOperationName}(") - .Append(firstArgType == GValue.SourceType ? "source" : "target") - .Append(", ") - .Append(string.Join(", ", - requiredInput.Select(arg => $"{SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()}") - .ToArray())); - - if (outParameters != null) - { - if (requiredInput.Length > 0) - { - result.Append(", "); - } - - result.Append(string.Join(", ", - outParameters.Select(arg => - $"out {SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()}") - .ToArray())); - } - - if (optionalInput.Length > 0) - { - if (requiredInput.Length > 0 || outParameters != null) - { - result.Append(", "); - } - - result.Append(string.Join(", ", - optionalInput.Select(arg => - $"{SafeIdentifier(arg.Name).ToPascalCase().FirstLetterToLower()}") - .ToArray())); - } - - result.AppendLine(");"); - - if (firstArgType == GValue.SourceType) - { - result.AppendLine() - .AppendLine($"{indent} image.OnPostClose += () => source.Dispose();") - .AppendLine() - .AppendLine($"{indent} return image;"); - } - - result - .AppendLine($"{indent}}}"); + .AppendLine($"{indent} image.OnPostClose += () => source.Dispose();") + .AppendLine() + .AppendLine($"{indent} return image;"); } - // Create method overloading if necessary - if (optionalOutput.Length > 0 && outParameters == null) - { - result.AppendLine() - .Append(GenerateFunction(operationName, indent, mutable, new[] { optionalOutput[0] })); - } - else if (outParameters != null && outParameters.Count != optionalOutput.Length) - { - result.AppendLine() - .Append(GenerateFunction(operationName, indent, mutable, - optionalOutput.Take(outParameters.Count + 1).ToArray())); - } + result + .AppendLine($"{indent}}}"); + } - return result.ToString(); + // Create method overloading if necessary + if (optionalOutput.Length > 0 && outParameters == null) + { + result.AppendLine() + .Append(GenerateFunction(operationName, indent, mutable, new[] { optionalOutput[0] })); + } + else if (outParameters != null && outParameters.Count != optionalOutput.Length) + { + result.AppendLine() + .Append(GenerateFunction(operationName, indent, mutable, + optionalOutput.Take(outParameters.Count + 1).ToArray())); } - /// - /// Generate the `Image.Generated.cs` file. - /// - /// - /// This is used to generate the `Image.Generated.cs` file (). - /// - /// Indentation level. - /// The `Image.Generated.cs` as string. - private string Generate(string indent = " ") + return result.ToString(); + } + + /// + /// Generate the `Image.Generated.cs` file. + /// + /// + /// This is used to generate the `Image.Generated.cs` file (). + /// + /// Indentation level. + /// The `Image.Generated.cs` as string. + private string Generate(string indent = " ") + { + // remove operations we have to wrap by hand + var exclude = new[] { - // remove operations we have to wrap by hand - var exclude = new[] - { - "scale", - "ifthenelse", - "bandjoin", - "bandrank", - "composite", - "case" - }; + "scale", + "ifthenelse", + "bandjoin", + "bandrank", + "composite", + "case" + }; - // get the list of all nicknames we can generate docstrings for. - var allNickNames = _allNickNames.Where(x => !exclude.Contains(x)).ToList(); + // get the list of all nicknames we can generate docstrings for. + var allNickNames = _allNickNames.Where(x => !exclude.Contains(x)).ToList(); - const string preamble = @"//------------------------------------------------------------------------------ + const string preamble = @"//------------------------------------------------------------------------------ // // This code was generated by a tool. // libvips version: {0} @@ -787,70 +787,69 @@ private string Generate(string indent = " ") // //------------------------------------------------------------------------------"; - var stringBuilder = - new StringBuilder(string.Format(preamble, - $"{NetVips.Version(0)}.{NetVips.Version(1)}.{NetVips.Version(2)}")); - stringBuilder.AppendLine() - .AppendLine() - .AppendLine("namespace NetVips") - .AppendLine("{") - .AppendLine(" using System.IO;") - .AppendLine() - .AppendLine(" public partial class Image") - .AppendLine(" {") - .AppendLine($"{indent}#region auto-generated functions") - .AppendLine(); - foreach (var nickname in allNickNames) + var stringBuilder = + new StringBuilder(string.Format(preamble, + $"{NetVips.Version(0)}.{NetVips.Version(1)}.{NetVips.Version(2)}")); + stringBuilder.AppendLine() + .AppendLine() + .AppendLine("using System.IO;") + .AppendLine("") + .AppendLine("namespace NetVips;") + .AppendLine("") + .AppendLine("public partial class Image") + .AppendLine("{") + .AppendLine($"{indent}#region auto-generated functions") + .AppendLine(); + foreach (var nickname in allNickNames) + { + try { - try - { - stringBuilder.AppendLine(GenerateFunction(nickname, indent)); - } - catch (ArgumentException) - { - // ignore - } - catch (Exception e) - { - Console.WriteLine(e.Message); - } + stringBuilder.AppendLine(GenerateFunction(nickname, indent)); } - - stringBuilder.AppendLine($"{indent}#endregion") - .AppendLine() - .AppendLine($"{indent}#region auto-generated properties") - .AppendLine(); - - var tmpFile = Image.NewTempFile("%s.v"); - var allProperties = tmpFile.GetFields(); - foreach (var property in allProperties) + catch (ArgumentException) { - var type = GTypeToCSharp(property, tmpFile.GetTypeOf(property)); - stringBuilder.AppendLine($"{indent}/// ") - .AppendLine($"{indent}/// {tmpFile.GetBlurb(property)}") - .AppendLine($"{indent}/// ") - .AppendLine($"{indent}public {type} {property.ToPascalCase()} => ({type})Get(\"{property}\");") - .AppendLine(); + // ignore } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + } - stringBuilder.AppendLine($"{indent}#endregion") - .AppendLine(" }") - .AppendLine("}"); + stringBuilder.AppendLine($"{indent}#endregion") + .AppendLine() + .AppendLine($"{indent}#region auto-generated properties") + .AppendLine(); - return stringBuilder.ToString(); + var tmpFile = Image.NewTempFile("%s.v"); + var allProperties = tmpFile.GetFields(); + foreach (var property in allProperties) + { + var type = GTypeToCSharp(property, tmpFile.GetTypeOf(property)); + stringBuilder.AppendLine($"{indent}/// ") + .AppendLine($"{indent}/// {tmpFile.GetBlurb(property)}") + .AppendLine($"{indent}/// ") + .AppendLine($"{indent}public {type} {property.ToPascalCase()} => ({type})Get(\"{property}\");") + .AppendLine(); } - /// - /// Generate the `MutableImage.Generated.cs` file. - /// - /// - /// This is used to generate the `MutableImage.Generated.cs` file (). - /// - /// Indentation level. - /// The `MutableImage.Generated.cs` as string. - private string GenerateMutable(string indent = " ") - { - const string preamble = @"//------------------------------------------------------------------------------ + stringBuilder.AppendLine($"{indent}#endregion") + .Append('}'); + + return stringBuilder.ToString(); + } + + /// + /// Generate the `MutableImage.Generated.cs` file. + /// + /// + /// This is used to generate the `MutableImage.Generated.cs` file (). + /// + /// Indentation level. + /// The `MutableImage.Generated.cs` as string. + private string GenerateMutable(string indent = " ") + { + const string preamble = @"//------------------------------------------------------------------------------ // // This code was generated by a tool. // libvips version: {0} @@ -860,46 +859,44 @@ private string GenerateMutable(string indent = " ") // //------------------------------------------------------------------------------"; - var stringBuilder = - new StringBuilder(string.Format(preamble, - $"{NetVips.Version(0)}.{NetVips.Version(1)}.{NetVips.Version(2)}")); - stringBuilder.AppendLine() - .AppendLine() - .AppendLine("namespace NetVips") - .AppendLine("{") - .AppendLine(" public sealed partial class MutableImage") - .AppendLine(" {") - .AppendLine($"{indent}#region auto-generated functions") - .AppendLine(); - foreach (var nickname in _allNickNames) + var stringBuilder = + new StringBuilder(string.Format(preamble, + $"{NetVips.Version(0)}.{NetVips.Version(1)}.{NetVips.Version(2)}")); + stringBuilder.AppendLine() + .AppendLine() + .AppendLine("namespace NetVips;") + .AppendLine() + .AppendLine("public sealed partial class MutableImage") + .AppendLine("{") + .AppendLine($"{indent}#region auto-generated functions") + .AppendLine(); + foreach (var nickname in _allNickNames) + { + try { - try - { - stringBuilder.AppendLine(GenerateFunction(nickname, indent, true)); - } - catch (ArgumentException) - { - // ignore - } - catch (Exception e) - { - Console.WriteLine(e.Message); - } + stringBuilder.AppendLine(GenerateFunction(nickname, indent, true)); + } + catch (ArgumentException) + { + // ignore } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + } - stringBuilder.AppendLine($"{indent}#endregion") - .AppendLine(" }") - .AppendLine("}"); + stringBuilder.AppendLine($"{indent}#endregion") + .Append('}'); - return stringBuilder.ToString(); - } + return stringBuilder.ToString(); + } - public void Execute(string[] args) - { - File.WriteAllText("Image.Generated.cs", Generate()); - File.WriteAllText("MutableImage.Generated.cs", GenerateMutable()); + public void Execute(string[] args) + { + File.WriteAllText("Image.Generated.cs", Generate()); + File.WriteAllText("MutableImage.Generated.cs", GenerateMutable()); - Console.WriteLine("See *.Generated.cs"); - } + Console.WriteLine("See *.Generated.cs"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/GenerateImageOperators.cs b/samples/NetVips.Samples/Samples/GenerateImageOperators.cs index 2d2babc0..5b565f39 100644 --- a/samples/NetVips.Samples/Samples/GenerateImageOperators.cs +++ b/samples/NetVips.Samples/Samples/GenerateImageOperators.cs @@ -1,291 +1,291 @@ -namespace NetVips.Samples -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; - public class GenerateImageOperators : ISample - { - public string Name => "Generate operator overloads"; - public string Category => "Internal"; +namespace NetVips.Samples; - private const string Indent = " "; +public class GenerateImageOperators : ISample +{ + public string Name => "Generate operator overloads"; + public string Category => "Internal"; - private readonly string _docstring = $$""" - {{Indent}}/// - {{Indent}}/// This operation {0}. - {{Indent}}/// - {{Indent}}/// {1}. - {{Indent}}/// {2}. - {{Indent}}/// A new . - """; + private const string Indent = " "; - /// - /// Make a C#-style docstring + operator overload. - /// - /// - /// This is used to generate the functions in . - /// - /// Overload operator. - /// The type to overload for. - /// Is the image located on the left side of the operand? - /// A C#-style docstring + operator overload. - private string GenerateOverload(string operatorStr, string type, bool invert = false) - { - const string calculatesSummary = "calculates {0} "; - const string imageCref = ""; + private readonly string _docstring = $$""" + {{Indent}}/// + {{Indent}}/// This operation {0}. + {{Indent}}/// + {{Indent}}/// {1}. + {{Indent}}/// {2}. + {{Indent}}/// A new . + """; - string operation; - string leftArg; - string rightArg; + /// + /// Make a C#-style docstring + operator overload. + /// + /// + /// This is used to generate the functions in . + /// + /// Overload operator. + /// The type to overload for. + /// Is the image located on the left side of the operand? + /// A C#-style docstring + operator overload. + private string GenerateOverload(string operatorStr, string type, bool invert = false) + { + const string calculatesSummary = "calculates {0} "; + const string imageCref = ""; - var leftParam = invert ? "right" : "left"; - var rightParam = invert ? "left" : "right"; + string operation; + string leftArg; + string rightArg; - string summary; + var leftParam = invert ? "right" : "left"; + var rightParam = invert ? "left" : "right"; - var isImage = type == "Image"; + string summary; - switch (operatorStr) - { - case "+": - summary = string.Format(calculatesSummary, operatorStr); - operation = isImage ? "add" : "linear"; - leftArg = isImage ? rightParam : "1.0"; - rightArg = isImage ? string.Empty : rightParam; - break; - case "-" when invert: - summary = string.Format(calculatesSummary, operatorStr); - operation = "linear"; - leftArg = "-1.0"; - rightArg = rightParam; - break; - case "-": - summary = string.Format(calculatesSummary, operatorStr); - operation = isImage ? "subtract" : "linear"; - leftArg = isImage ? rightParam : "1.0"; - rightArg = isImage ? string.Empty : - type.EndsWith("[]") ? $"{rightParam}.Negate()" : $"-{rightParam}"; - break; - case "*": - summary = string.Format(calculatesSummary, operatorStr); - operation = isImage ? "multiply" : "linear"; - leftArg = rightParam; - rightArg = isImage ? string.Empty : "0.0"; - break; - case "/" when invert: - summary = string.Format(calculatesSummary, operatorStr); - operation = "linear"; - leftParam += ".Pow(-1.0)"; - leftArg = rightParam; - rightArg = "0.0"; - break; - case "/": - summary = string.Format(calculatesSummary, operatorStr); - operation = isImage ? "divide" : "linear"; - leftArg = isImage ? rightParam : - type.EndsWith("[]") ? $"{rightParam}.Invert()" : $"1.0 / {rightParam}"; - rightArg = isImage ? string.Empty : "0.0"; - break; - case "%": - summary = string.Format(calculatesSummary, operatorStr) + - $"\n{Indent}/// (remainder after integer division)"; - operation = isImage ? "remainder" : "remainder_const"; - leftArg = rightParam; - rightArg = string.Empty; - break; - case "&": - summary = "computes the logical bitwise AND of its operands"; - operation = isImage ? "boolean" : "boolean_const"; - leftArg = isImage ? rightParam : "Enums.OperationBoolean.And"; - rightArg = isImage ? "Enums.OperationBoolean.And" : rightParam; - break; - case "|": - summary = "computes the bitwise OR of its operands"; - operation = isImage ? "boolean" : "boolean_const"; - leftArg = isImage ? rightParam : "Enums.OperationBoolean.Or"; - rightArg = isImage ? "Enums.OperationBoolean.Or" : rightParam; - break; - case "^": - summary = "computes the bitwise exclusive-OR of its operands"; - operation = isImage ? "boolean" : "boolean_const"; - leftArg = isImage ? rightParam : "Enums.OperationBoolean.Eor"; - rightArg = isImage ? "Enums.OperationBoolean.Eor" : rightParam; - break; - case "==": - summary = "compares two images on equality"; - operation = "relational_const"; - leftArg = "Enums.OperationRelational.Equal"; - rightArg = rightParam; - break; - case "!=": - summary = "compares two images on inequality"; - operation = "relational_const"; - leftArg = "Enums.OperationRelational.Noteq"; - rightArg = rightParam; - break; - case "<<": - summary = "shifts its first operand left by the number of bits specified by its second operand"; - operation = "boolean_const"; - leftArg = "Enums.OperationBoolean.Lshift"; - rightArg = rightParam; - break; - case ">>": - summary = "shifts its first operand right by the number of bits specified by its second operand"; - operation = "boolean_const"; - leftArg = "Enums.OperationBoolean.Rshift"; - rightArg = rightParam; - break; - case "<" when invert: - summary = "compares if the left operand is less than the right operand"; - operation = "relational_const"; - leftArg = "Enums.OperationRelational.More"; - rightArg = rightParam; - break; - case "<": - summary = "compares if the left operand is less than the right operand"; - operation = isImage ? "relational" : "relational_const"; - leftArg = isImage ? rightParam : "Enums.OperationRelational.Less"; - rightArg = isImage ? "Enums.OperationRelational.Less" : rightParam; - break; - case ">" when invert: - summary = "compares if the left operand is greater than the right operand"; - operation = "relational_const"; - leftArg = "Enums.OperationRelational.Less"; - rightArg = rightParam; - break; - case ">": - summary = "compares if the left operand is greater than the right operand"; - operation = isImage ? "relational" : "relational_const"; - leftArg = isImage ? rightParam : "Enums.OperationRelational.More"; - rightArg = isImage ? "Enums.OperationRelational.More" : rightParam; - break; - case "<=" when invert: - summary = "compares if the left operand is less than or equal to the right operand"; - operation = "relational_const"; - leftArg = "Enums.OperationRelational.Moreeq"; - rightArg = rightParam; - break; - case "<=": - summary = "compares if the left operand is less than or equal to the right operand"; - operation = isImage ? "relational" : "relational_const"; - leftArg = isImage ? rightParam : "Enums.OperationRelational.Lesseq"; - rightArg = isImage ? "Enums.OperationRelational.Lesseq" : rightParam; - break; - case ">=" when invert: - summary = "compares if the left operand is greater than or equal to the right operand"; - operation = "relational_const"; - leftArg = "Enums.OperationRelational.Lesseq"; - rightArg = rightParam; - break; - case ">=": - summary = "compares if the left operand is greater than or equal to the right operand"; - operation = isImage ? "relational" : "relational_const"; - leftArg = isImage ? rightParam : "Enums.OperationRelational.Moreeq"; - rightArg = isImage ? "Enums.OperationRelational.Moreeq" : rightParam; - break; - default: - throw new ArgumentOutOfRangeException(nameof(operatorStr), operatorStr, "Operator out of range"); - } + var isImage = type == "Image"; - string leftDesc; - string rightDesc; + switch (operatorStr) + { + case "+": + summary = string.Format(calculatesSummary, operatorStr); + operation = isImage ? "add" : "linear"; + leftArg = isImage ? rightParam : "1.0"; + rightArg = isImage ? string.Empty : rightParam; + break; + case "-" when invert: + summary = string.Format(calculatesSummary, operatorStr); + operation = "linear"; + leftArg = "-1.0"; + rightArg = rightParam; + break; + case "-": + summary = string.Format(calculatesSummary, operatorStr); + operation = isImage ? "subtract" : "linear"; + leftArg = isImage ? rightParam : "1.0"; + rightArg = isImage ? string.Empty : + type.EndsWith("[]") ? $"{rightParam}.Negate()" : $"-{rightParam}"; + break; + case "*": + summary = string.Format(calculatesSummary, operatorStr); + operation = isImage ? "multiply" : "linear"; + leftArg = rightParam; + rightArg = isImage ? string.Empty : "0.0"; + break; + case "/" when invert: + summary = string.Format(calculatesSummary, operatorStr); + operation = "linear"; + leftParam += ".Pow(-1.0)"; + leftArg = rightParam; + rightArg = "0.0"; + break; + case "/": + summary = string.Format(calculatesSummary, operatorStr); + operation = isImage ? "divide" : "linear"; + leftArg = isImage ? rightParam : + type.EndsWith("[]") ? $"{rightParam}.Invert()" : $"1.0 / {rightParam}"; + rightArg = isImage ? string.Empty : "0.0"; + break; + case "%": + summary = string.Format(calculatesSummary, operatorStr) + + $"\n{Indent}/// (remainder after integer division)"; + operation = isImage ? "remainder" : "remainder_const"; + leftArg = rightParam; + rightArg = string.Empty; + break; + case "&": + summary = "computes the logical bitwise AND of its operands"; + operation = isImage ? "boolean" : "boolean_const"; + leftArg = isImage ? rightParam : "Enums.OperationBoolean.And"; + rightArg = isImage ? "Enums.OperationBoolean.And" : rightParam; + break; + case "|": + summary = "computes the bitwise OR of its operands"; + operation = isImage ? "boolean" : "boolean_const"; + leftArg = isImage ? rightParam : "Enums.OperationBoolean.Or"; + rightArg = isImage ? "Enums.OperationBoolean.Or" : rightParam; + break; + case "^": + summary = "computes the bitwise exclusive-OR of its operands"; + operation = isImage ? "boolean" : "boolean_const"; + leftArg = isImage ? rightParam : "Enums.OperationBoolean.Eor"; + rightArg = isImage ? "Enums.OperationBoolean.Eor" : rightParam; + break; + case "==": + summary = "compares two images on equality"; + operation = "relational_const"; + leftArg = "Enums.OperationRelational.Equal"; + rightArg = rightParam; + break; + case "!=": + summary = "compares two images on inequality"; + operation = "relational_const"; + leftArg = "Enums.OperationRelational.Noteq"; + rightArg = rightParam; + break; + case "<<": + summary = "shifts its first operand left by the number of bits specified by its second operand"; + operation = "boolean_const"; + leftArg = "Enums.OperationBoolean.Lshift"; + rightArg = rightParam; + break; + case ">>": + summary = "shifts its first operand right by the number of bits specified by its second operand"; + operation = "boolean_const"; + leftArg = "Enums.OperationBoolean.Rshift"; + rightArg = rightParam; + break; + case "<" when invert: + summary = "compares if the left operand is less than the right operand"; + operation = "relational_const"; + leftArg = "Enums.OperationRelational.More"; + rightArg = rightParam; + break; + case "<": + summary = "compares if the left operand is less than the right operand"; + operation = isImage ? "relational" : "relational_const"; + leftArg = isImage ? rightParam : "Enums.OperationRelational.Less"; + rightArg = isImage ? "Enums.OperationRelational.Less" : rightParam; + break; + case ">" when invert: + summary = "compares if the left operand is greater than the right operand"; + operation = "relational_const"; + leftArg = "Enums.OperationRelational.Less"; + rightArg = rightParam; + break; + case ">": + summary = "compares if the left operand is greater than the right operand"; + operation = isImage ? "relational" : "relational_const"; + leftArg = isImage ? rightParam : "Enums.OperationRelational.More"; + rightArg = isImage ? "Enums.OperationRelational.More" : rightParam; + break; + case "<=" when invert: + summary = "compares if the left operand is less than or equal to the right operand"; + operation = "relational_const"; + leftArg = "Enums.OperationRelational.Moreeq"; + rightArg = rightParam; + break; + case "<=": + summary = "compares if the left operand is less than or equal to the right operand"; + operation = isImage ? "relational" : "relational_const"; + leftArg = isImage ? rightParam : "Enums.OperationRelational.Lesseq"; + rightArg = isImage ? "Enums.OperationRelational.Lesseq" : rightParam; + break; + case ">=" when invert: + summary = "compares if the left operand is greater than or equal to the right operand"; + operation = "relational_const"; + leftArg = "Enums.OperationRelational.Lesseq"; + rightArg = rightParam; + break; + case ">=": + summary = "compares if the left operand is greater than or equal to the right operand"; + operation = isImage ? "relational" : "relational_const"; + leftArg = isImage ? rightParam : "Enums.OperationRelational.Moreeq"; + rightArg = isImage ? "Enums.OperationRelational.Moreeq" : rightParam; + break; + default: + throw new ArgumentOutOfRangeException(nameof(operatorStr), operatorStr, "Operator out of range"); + } - switch (type) - { - case "Image": - leftDesc = $"Left {imageCref}"; - rightDesc = $"Right {imageCref}"; - break; - case "double[]" when invert: - leftDesc = "Left double array"; - rightDesc = $"Right {imageCref}"; - break; - case "double[]": - leftDesc = $"Left {imageCref}"; - rightDesc = "Right double array"; - break; - case "int[]" when invert: - leftDesc = "Left integer array"; - rightDesc = $"Right {imageCref}"; - break; - case "int[]": - leftDesc = $"Left {imageCref}"; - rightDesc = "Right integer array"; - break; - case "double" when invert: - leftDesc = "Left double constant"; - rightDesc = $"Right {imageCref}"; - break; - case "double": - leftDesc = $"Left {imageCref}"; - rightDesc = "Right double constant"; - break; - case "int": - leftDesc = $"Left {imageCref}"; - rightDesc = "The number of bits"; - break; - default: - throw new ArgumentOutOfRangeException(nameof(type), type, "Type out of range"); - } + string leftDesc; + string rightDesc; - if (operatorStr == "==" || operatorStr == "!=") - { - leftDesc += " to compare"; - rightDesc += " to compare"; - } + switch (type) + { + case "Image": + leftDesc = $"Left {imageCref}"; + rightDesc = $"Right {imageCref}"; + break; + case "double[]" when invert: + leftDesc = "Left double array"; + rightDesc = $"Right {imageCref}"; + break; + case "double[]": + leftDesc = $"Left {imageCref}"; + rightDesc = "Right double array"; + break; + case "int[]" when invert: + leftDesc = "Left integer array"; + rightDesc = $"Right {imageCref}"; + break; + case "int[]": + leftDesc = $"Left {imageCref}"; + rightDesc = "Right integer array"; + break; + case "double" when invert: + leftDesc = "Left double constant"; + rightDesc = $"Right {imageCref}"; + break; + case "double": + leftDesc = $"Left {imageCref}"; + rightDesc = "Right double constant"; + break; + case "int": + leftDesc = $"Left {imageCref}"; + rightDesc = "The number of bits"; + break; + default: + throw new ArgumentOutOfRangeException(nameof(type), type, "Type out of range"); + } - var result = new StringBuilder(string.Format(_docstring, summary, leftDesc, rightDesc)); + if (operatorStr == "==" || operatorStr == "!=") + { + leftDesc += " to compare"; + rightDesc += " to compare"; + } - result - .AppendLine() - .AppendLine(invert - ? $"{Indent}public static Image operator {operatorStr}({type} left, Image right) =>" - : $"{Indent}public static Image operator {operatorStr}(Image left, {type} right) =>") - .AppendLine( - string.IsNullOrEmpty(rightArg) - ? $"{Indent} {leftParam}.Call(\"{operation}\", {leftArg}) as Image;" - : $"{Indent} {leftParam}.Call(\"{operation}\", {leftArg}, {rightArg}) as Image;"); + var result = new StringBuilder(string.Format(_docstring, summary, leftDesc, rightDesc)); - return result.ToString(); - } + result + .AppendLine() + .AppendLine(invert + ? $"{Indent}public static Image operator {operatorStr}({type} left, Image right) =>" + : $"{Indent}public static Image operator {operatorStr}(Image left, {type} right) =>") + .AppendLine( + string.IsNullOrEmpty(rightArg) + ? $"{Indent} {leftParam}.Call(\"{operation}\", {leftArg}) as Image;" + : $"{Indent} {leftParam}.Call(\"{operation}\", {leftArg}, {rightArg}) as Image;"); + + return result.ToString(); + } - /// - /// Generate the `Image.Operators.cs` file. - /// - /// - /// This is used to generate the `Image.Operators.cs` file (). - /// Use it with something like: - /// - /// File.WriteAllText("Image.Operators.cs", GenerateOperators()); - /// - /// - /// The `Image.Operators.cs` as string. - public string GenerateOperators() + /// + /// Generate the `Image.Operators.cs` file. + /// + /// + /// This is used to generate the `Image.Operators.cs` file (). + /// Use it with something like: + /// + /// File.WriteAllText("Image.Operators.cs", GenerateOperators()); + /// + /// + /// The `Image.Operators.cs` as string. + public string GenerateOperators() + { + // generate list of all operator overloads and supported types + var allOverloads = new Dictionary { - // generate list of all operator overloads and supported types - var allOverloads = new Dictionary - { - {"+", new[] {"Image", "double", "double[]", "int[]"}}, - {"-", new[] {"Image", "double", "double[]", "int[]"}}, - {"*", new[] {"Image", "double", "double[]", "int[]"}}, - {"/", new[] {"Image", "double", "double[]", "int[]"}}, - {"%", new[] {"Image", "double", "double[]", "int[]"}}, - {"&", new[] {"Image", "double", "double[]", "int[]"}}, - {"|", new[] {"Image", "double", "double[]", "int[]"}}, - {"^", new[] {"Image", "double", "double[]", "int[]"}}, - {"<<", new[] {"int"}}, - {">>", new[] {"int"}}, - {"==", new[] {"double", "double[]", "int[]"}}, - {"!=", new[] {"double", "double[]", "int[]"}}, - {"<", new[] {"Image", "double", "double[]", "int[]"}}, - {">", new[] {"Image", "double", "double[]", "int[]"}}, - {"<=", new[] {"Image", "double", "double[]", "int[]"}}, - {">=", new[] {"Image", "double", "double[]", "int[]"}} - }; + {"+", new[] {"Image", "double", "double[]", "int[]"}}, + {"-", new[] {"Image", "double", "double[]", "int[]"}}, + {"*", new[] {"Image", "double", "double[]", "int[]"}}, + {"/", new[] {"Image", "double", "double[]", "int[]"}}, + {"%", new[] {"Image", "double", "double[]", "int[]"}}, + {"&", new[] {"Image", "double", "double[]", "int[]"}}, + {"|", new[] {"Image", "double", "double[]", "int[]"}}, + {"^", new[] {"Image", "double", "double[]", "int[]"}}, + {"<<", new[] {"int"}}, + {">>", new[] {"int"}}, + {"==", new[] {"double", "double[]", "int[]"}}, + {"!=", new[] {"double", "double[]", "int[]"}}, + {"<", new[] {"Image", "double", "double[]", "int[]"}}, + {">", new[] {"Image", "double", "double[]", "int[]"}}, + {"<=", new[] {"Image", "double", "double[]", "int[]"}}, + {">=", new[] {"Image", "double", "double[]", "int[]"}} + }; - const string preamble = @"//------------------------------------------------------------------------------ + const string preamble = @"//------------------------------------------------------------------------------ // // This code was generated by a tool. // @@ -294,73 +294,71 @@ public string GenerateOperators() // //------------------------------------------------------------------------------"; - var stringBuilder = new StringBuilder(preamble); - stringBuilder.AppendLine() - .AppendLine() - .AppendLine("namespace NetVips") - .AppendLine("{") - .AppendLine(" public sealed partial class Image") - .AppendLine(" {") - .AppendLine($"{Indent}#region auto-generated operator overloads") - .AppendLine(); + var stringBuilder = new StringBuilder(preamble); + stringBuilder.AppendLine() + .AppendLine() + .AppendLine("namespace NetVips;") + .AppendLine() + .AppendLine("public partial class Image") + .AppendLine("{") + .AppendLine($"{Indent}#region auto-generated operator overloads") + .AppendLine(); - foreach (var (operatorStr, types) in allOverloads) + foreach (var (operatorStr, types) in allOverloads) + { + foreach (var type in types) { - foreach (var type in types) + if (type != "Image" && types.Length > 1) { - if (type != "Image" && types.Length > 1) - { - stringBuilder.AppendLine(GenerateOverload(operatorStr, type, true)); - } - - // We only generate the inverted types of the `==` and `!=` operators - // to avoid conflicts with `null` checks (for e.g. `image == null`). - // See: `Equal()` and `NotEqual()` for comparison with the other types. - if (operatorStr == "==" || operatorStr == "!=") - { - continue; - } + stringBuilder.AppendLine(GenerateOverload(operatorStr, type, true)); + } - stringBuilder.AppendLine(GenerateOverload(operatorStr, type)); + // We only generate the inverted types of the `==` and `!=` operators + // to avoid conflicts with `null` checks (for e.g. `image == null`). + // See: `Equal()` and `NotEqual()` for comparison with the other types. + if (operatorStr == "==" || operatorStr == "!=") + { + continue; } + + stringBuilder.AppendLine(GenerateOverload(operatorStr, type)); } + } - stringBuilder.AppendLine($"{Indent}/// ") - .AppendLine( - $"{Indent}/// Returns a value indicating whether a given is definitely .") - .AppendLine($"{Indent}/// ") - .AppendLine($"{Indent}/// The image to check.") - .AppendLine( - $"{Indent}/// if is definitely ; otherwise, .") - .AppendLine($"{Indent}public static bool operator true(Image image) =>") - .AppendLine( - $"{Indent} // Always evaluate to false so that each side of the && equation is evaluated") - .AppendLine($"{Indent} false;") - .AppendLine(); + stringBuilder.AppendLine($"{Indent}/// ") + .AppendLine( + $"{Indent}/// Returns a value indicating whether a given is definitely .") + .AppendLine($"{Indent}/// ") + .AppendLine($"{Indent}/// The image to check.") + .AppendLine( + $"{Indent}/// if is definitely ; otherwise, .") + .AppendLine($"{Indent}public static bool operator true(Image image) =>") + .AppendLine( + $"{Indent} // Always evaluate to false so that each side of the && equation is evaluated") + .AppendLine($"{Indent} false;") + .AppendLine(); - stringBuilder.AppendLine($"{Indent}/// ") - .AppendLine( - $"{Indent}/// Returns a value indicating whether a given is definitely .") - .AppendLine($"{Indent}/// ") - .AppendLine($"{Indent}/// The image to check.") - .AppendLine( - $"{Indent}/// if is definitely ; otherwise, .") - .AppendLine($"{Indent}public static bool operator false(Image image) =>") - .AppendLine( - $"{Indent} // Always evaluate to false so that each side of the && equation is evaluated") - .AppendLine($"{Indent} false;") - .AppendLine(); + stringBuilder.AppendLine($"{Indent}/// ") + .AppendLine( + $"{Indent}/// Returns a value indicating whether a given is definitely .") + .AppendLine($"{Indent}/// ") + .AppendLine($"{Indent}/// The image to check.") + .AppendLine( + $"{Indent}/// if is definitely ; otherwise, .") + .AppendLine($"{Indent}public static bool operator false(Image image) =>") + .AppendLine( + $"{Indent} // Always evaluate to false so that each side of the && equation is evaluated") + .AppendLine($"{Indent} false;") + .AppendLine(); - stringBuilder.AppendLine($"{Indent}#endregion") - .AppendLine(" }") - .AppendLine("}"); - return stringBuilder.ToString(); - } + stringBuilder.AppendLine($"{Indent}#endregion") + .Append('}'); + return stringBuilder.ToString(); + } - public void Execute(string[] args) - { - File.WriteAllText("Image.Operators.cs", GenerateOperators()); - Console.WriteLine("See Image.Operators.cs"); - } + public void Execute(string[] args) + { + File.WriteAllText("Image.Operators.cs", GenerateOperators()); + Console.WriteLine("See Image.Operators.cs"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/HelloWorld.cs b/samples/NetVips.Samples/Samples/HelloWorld.cs index 9c43de29..22dd64b2 100644 --- a/samples/NetVips.Samples/Samples/HelloWorld.cs +++ b/samples/NetVips.Samples/Samples/HelloWorld.cs @@ -1,21 +1,20 @@ -namespace NetVips.Samples +using System; + +namespace NetVips.Samples; + +/// +/// From: https://github.com/libvips/lua-vips/blob/master/example/hello-world.lua +/// +public class HelloWorld : ISample { - using System; + public string Name => "Hello world"; + public string Category => "Create"; - /// - /// From: https://github.com/libvips/lua-vips/blob/master/example/hello-world.lua - /// - public class HelloWorld : ISample + public void Execute(string[] args) { - public string Name => "Hello world"; - public string Category => "Create"; - - public void Execute(string[] args) - { - using var image = Image.Text("Hello World!", dpi: 300); - image.WriteToFile("hello-world.png"); + using var image = Image.Text("Hello World!", dpi: 300); + image.WriteToFile("hello-world.png"); - Console.WriteLine("See hello-world.png"); - } + Console.WriteLine("See hello-world.png"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/IdentifyExtension.cs b/samples/NetVips.Samples/Samples/IdentifyExtension.cs index a17b912e..e6729e33 100644 --- a/samples/NetVips.Samples/Samples/IdentifyExtension.cs +++ b/samples/NetVips.Samples/Samples/IdentifyExtension.cs @@ -1,81 +1,80 @@ -namespace NetVips.Samples +using System; +using System.IO; +using System.Text; + +namespace NetVips.Samples; + +public class IdentifyExtension : ISample { - using System; - using System.IO; - using System.Text; + public string Name => "Identify image extension"; + public string Category => "Utils"; - public class IdentifyExtension : ISample + /// + /// Get image extension from a buffer. + /// + /// Buffer to check. + /// The image extension, or . + public string GetExtension(byte[] buffer) { - public string Name => "Identify image extension"; - public string Category => "Utils"; + var loader = Image.FindLoadBuffer(buffer); - /// - /// Get image extension from a buffer. - /// - /// Buffer to check. - /// The image extension, or . - public string GetExtension(byte[] buffer) + if (loader == null) { - var loader = Image.FindLoadBuffer(buffer); - - if (loader == null) - { - Console.WriteLine("Couldn't identify image extension"); - return null; - } + Console.WriteLine("Couldn't identify image extension"); + return null; + } - const int startIndex = 15; // VipsForeignLoad - var suffixLength = - loader.EndsWith("Buffer") || loader.EndsWith("Source") ? 6 : 4 /* loader.EndsWith("File") */; + const int startIndex = 15; // VipsForeignLoad + var suffixLength = + loader.EndsWith("Buffer") || loader.EndsWith("Source") ? 6 : 4 /* loader.EndsWith("File") */; - return loader.Substring(startIndex, - loader.Length - startIndex - suffixLength) - .ToLower(); - } + return loader.Substring(startIndex, + loader.Length - startIndex - suffixLength) + .ToLower(); + } - /// - /// Get image extension of a non-truncated buffer. - /// - /// Buffer to check. - /// The image extension, or . - public string GetExtensionNonTruncated(byte[] buffer) + /// + /// Get image extension of a non-truncated buffer. + /// + /// Buffer to check. + /// The image extension, or . + public string GetExtensionNonTruncated(byte[] buffer) + { + try { - try - { - // The failOn option makes NetVips throw an exception on a file format error - using var image = Image.NewFromBuffer(buffer, failOn: Enums.FailOn.Error, access: Enums.Access.Sequential); + // The failOn option makes NetVips throw an exception on a file format error + using var image = Image.NewFromBuffer(buffer, failOn: Enums.FailOn.Error, access: Enums.Access.Sequential); - // Calculate the average pixel value. That way you are guaranteed to read every pixel - // and the operation is cheap. - var avg = image.Avg(); + // Calculate the average pixel value. That way you are guaranteed to read every pixel + // and the operation is cheap. + var avg = image.Avg(); - // Unfortunately, vips-loader is the operation nickname, rather - // than the canonical name returned by vips_foreign_find_load(). - var vipsLoader = (string)image.Get("vips-loader"); - var suffixLength = vipsLoader.EndsWith("load_buffer") || vipsLoader.EndsWith("load_source") ? 11 : 4; + // Unfortunately, vips-loader is the operation nickname, rather + // than the canonical name returned by vips_foreign_find_load(). + var vipsLoader = (string)image.Get("vips-loader"); + var suffixLength = vipsLoader.EndsWith("load_buffer") || vipsLoader.EndsWith("load_source") ? 11 : 4; - return vipsLoader[..^suffixLength]; - } - catch (VipsException e) - { - Console.WriteLine($"Couldn't identify image extension: {e.Message}"); - return null; - } + return vipsLoader[..^suffixLength]; } - - public void Execute(string[] args) + catch (VipsException e) { - Console.WriteLine("FindLoadBuffer function (non-truncated buffer)"); - Console.WriteLine(GetExtension(File.ReadAllBytes("images/lichtenstein.jpg"))); + Console.WriteLine($"Couldn't identify image extension: {e.Message}"); + return null; + } + } - Console.WriteLine("vips-loader function (non-truncated buffer)"); - Console.WriteLine(GetExtensionNonTruncated(File.ReadAllBytes("images/lichtenstein.jpg"))); + public void Execute(string[] args) + { + Console.WriteLine("FindLoadBuffer function (non-truncated buffer)"); + Console.WriteLine(GetExtension(File.ReadAllBytes("images/lichtenstein.jpg"))); - Console.WriteLine("FindLoad function (truncated buffer)"); - Console.WriteLine(GetExtension(Encoding.UTF8.GetBytes("GIF89a"))); + Console.WriteLine("vips-loader function (non-truncated buffer)"); + Console.WriteLine(GetExtensionNonTruncated(File.ReadAllBytes("images/lichtenstein.jpg"))); - Console.WriteLine("vips-loader function (truncated buffer)"); - Console.WriteLine(GetExtensionNonTruncated(Encoding.UTF8.GetBytes("GIF89a"))); - } + Console.WriteLine("FindLoad function (truncated buffer)"); + Console.WriteLine(GetExtension(Encoding.UTF8.GetBytes("GIF89a"))); + + Console.WriteLine("vips-loader function (truncated buffer)"); + Console.WriteLine(GetExtensionNonTruncated(Encoding.UTF8.GetBytes("GIF89a"))); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/ImagePyramid.cs b/samples/NetVips.Samples/Samples/ImagePyramid.cs index 0c0d164c..57e6292f 100644 --- a/samples/NetVips.Samples/Samples/ImagePyramid.cs +++ b/samples/NetVips.Samples/Samples/ImagePyramid.cs @@ -1,43 +1,42 @@ -namespace NetVips.Samples +using System; +using System.Threading; + +namespace NetVips.Samples; + +public class ImagePyramid : ISample { - using System; - using System.Threading; + public string Name => "Image Pyramid"; + public string Category => "Create"; - public class ImagePyramid : ISample + public const int TileSize = 50; + public const string Filename = "images/sample2.v"; + + public void Execute(string[] args) { - public string Name => "Image Pyramid"; - public string Category => "Create"; + // Build test image + using var im = Image.NewFromFile(Filename, access: Enums.Access.Sequential); + using var test = im.Replicate(TileSize, TileSize); + + var cts = new CancellationTokenSource(); + cts.CancelAfter(5000); - public const int TileSize = 50; - public const string Filename = "images/sample2.v"; + var progress = new Progress(percent => Console.Write($"\r{percent}% complete")); + // Uncomment to kill the image after 5 sec + test.SetProgress(progress/*, cts.Token*/); - public void Execute(string[] args) + try { - // Build test image - using var im = Image.NewFromFile(Filename, access: Enums.Access.Sequential); - using var test = im.Replicate(TileSize, TileSize); - - var cts = new CancellationTokenSource(); - cts.CancelAfter(5000); - - var progress = new Progress(percent => Console.Write($"\r{percent}% complete")); - // Uncomment to kill the image after 5 sec - test.SetProgress(progress/*, cts.Token*/); - - try - { - // Save image pyramid - test.Dzsave("images/image-pyramid"); - } - catch (VipsException exception) - { - // Catch and log the VipsException, - // because we may block the evaluation of this image - Console.WriteLine("\n" + exception.Message); - } - - Console.WriteLine(); - Console.WriteLine("See images/image-pyramid.dzi"); + // Save image pyramid + test.Dzsave("images/image-pyramid"); } + catch (VipsException exception) + { + // Catch and log the VipsException, + // because we may block the evaluation of this image + Console.WriteLine("\n" + exception.Message); + } + + Console.WriteLine(); + Console.WriteLine("See images/image-pyramid.dzi"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/Indexer.cs b/samples/NetVips.Samples/Samples/Indexer.cs index a7330b6d..134bdb6c 100644 --- a/samples/NetVips.Samples/Samples/Indexer.cs +++ b/samples/NetVips.Samples/Samples/Indexer.cs @@ -1,35 +1,34 @@ -namespace NetVips.Samples +using System.Diagnostics; + +namespace NetVips.Samples; + +/// +/// From: https://github.com/libvips/php-vips/blob/d3cf61bd087bd9c00cfc5ae32f6ded7165963058/tests/ShortcutTest.php#L202 +/// +public class Indexer : ISample { - using System.Diagnostics; + public string Name => "Indexer"; + public string Category => "Create"; - /// - /// From: https://github.com/libvips/php-vips/blob/d3cf61bd087bd9c00cfc5ae32f6ded7165963058/tests/ShortcutTest.php#L202 - /// - public class Indexer : ISample + public void Execute(string[] args) { - public string Name => "Indexer"; - public string Category => "Create"; - - public void Execute(string[] args) - { - using var r = Image.NewFromArray(new[] { 1, 2, 3 }); - using var g = r + 1; - using var b = r + 2; - using var image = r.Bandjoin(g, b); + using var r = Image.NewFromArray(new[] { 1, 2, 3 }); + using var g = r + 1; + using var b = r + 2; + using var image = r.Bandjoin(g, b); - // replace band with image - using var array = image.NewFromImage(12, 13); - using var test = image.Mutate(x => x[1] = array); - using var band1 = test[0]; - using var band2 = test[1]; - using var band3 = test[2]; - using var band4 = test[3]; + // replace band with image + using var array = image.NewFromImage(12, 13); + using var test = image.Mutate(x => x[1] = array); + using var band1 = test[0]; + using var band2 = test[1]; + using var band3 = test[2]; + using var band4 = test[3]; - Debug.Assert(test.Bands == 4); - Debug.Assert(band1.Avg() == 2); - Debug.Assert(band2.Avg() == 12); - Debug.Assert(band3.Avg() == 13); - Debug.Assert(band4.Avg() == 4); - } + Debug.Assert(test.Bands == 4); + Debug.Assert(band1.Avg() == 2); + Debug.Assert(band2.Avg() == 12); + Debug.Assert(band3.Avg() == 13); + Debug.Assert(band4.Avg() == 4); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/LeakTest.cs b/samples/NetVips.Samples/Samples/LeakTest.cs index 47af142a..7947cae7 100644 --- a/samples/NetVips.Samples/Samples/LeakTest.cs +++ b/samples/NetVips.Samples/Samples/LeakTest.cs @@ -1,65 +1,64 @@ -namespace NetVips.Samples +using System; +using System.IO; + +namespace NetVips.Samples; + +/// +/// From: https://github.com/kleisauke/net-vips/issues/26 +/// +public class LeakTest : ISample { - using System; - using System.IO; + public string Name => "Leak test"; + public string Category => "Internal"; + + public const string Filename = "images/equus_quagga.jpg"; /// - /// From: https://github.com/kleisauke/net-vips/issues/26 + /// Load from memory buffer 10000 times. + /// + /// It runs in a fairly steady 60MB of ram for me. Watching the output, you see + /// stuff like: + /// + /// memory processing <NetVips.Image 4120x2747 uchar, 3 bands, srgb> + /// 24 vips objects known to net-vips + /// memory processing <NetVips.Image 4120x2747 uchar, 3 bands, srgb> + /// 21 vips objects known to net-vips + /// memory processing <NetVips.Image 4120x2747 uchar, 3 bands, srgb> + /// 23 vips objects known to net-vips + /// memory processing <NetVips.Image 4120x2747 uchar, 3 bands, srgb> + /// 16 vips objects known to net-vips + /// memory processing <NetVips.Image 4120x2747 uchar, 3 bands, srgb> + /// 7 vips objects known to net-vips + /// memory processing <NetVips.Image 4120x2747 uchar, 3 bands, srgb> + /// 7 vips objects known to net-vips + /// memory processing <NetVips.Image 4120x2747 uchar, 3 bands, srgb> + /// 9 vips objects known to net-vips + /// memory processing <NetVips.Image 4120x2747 uchar, 3 bands, srgb> + /// 4 vips objects known to net-vips + /// memory processing <NetVips.Image 4120x2747 uchar, 3 bands, srgb> + /// + /// So when around 25 vips objects are alive, the C# gc runs and they all get + /// flushed. + /// + /// If you want it to run in less ram than that, you'll need to expose the GC and + /// trigger it manually every so often. /// - public class LeakTest : ISample + /// Command-line arguments. + /// Result. + public void Execute(string[] args) { - public string Name => "Leak test"; - public string Category => "Internal"; + NetVips.Leak = true; - public const string Filename = "images/equus_quagga.jpg"; + Cache.Max = 0; - /// - /// Load from memory buffer 10000 times. - /// - /// It runs in a fairly steady 60MB of ram for me. Watching the output, you see - /// stuff like: - /// - /// memory processing <NetVips.Image 4120x2747 uchar, 3 bands, srgb> - /// 24 vips objects known to net-vips - /// memory processing <NetVips.Image 4120x2747 uchar, 3 bands, srgb> - /// 21 vips objects known to net-vips - /// memory processing <NetVips.Image 4120x2747 uchar, 3 bands, srgb> - /// 23 vips objects known to net-vips - /// memory processing <NetVips.Image 4120x2747 uchar, 3 bands, srgb> - /// 16 vips objects known to net-vips - /// memory processing <NetVips.Image 4120x2747 uchar, 3 bands, srgb> - /// 7 vips objects known to net-vips - /// memory processing <NetVips.Image 4120x2747 uchar, 3 bands, srgb> - /// 7 vips objects known to net-vips - /// memory processing <NetVips.Image 4120x2747 uchar, 3 bands, srgb> - /// 9 vips objects known to net-vips - /// memory processing <NetVips.Image 4120x2747 uchar, 3 bands, srgb> - /// 4 vips objects known to net-vips - /// memory processing <NetVips.Image 4120x2747 uchar, 3 bands, srgb> - /// - /// So when around 25 vips objects are alive, the C# gc runs and they all get - /// flushed. - /// - /// If you want it to run in less ram than that, you'll need to expose the GC and - /// trigger it manually every so often. - /// - /// Command-line arguments. - /// Result. - public void Execute(string[] args) - { - NetVips.Leak = true; + var imageBytes = File.ReadAllBytes(Filename); - Cache.Max = 0; - - var imageBytes = File.ReadAllBytes(Filename); - - for (var i = 0; i < 10000; i++) - { - using var img = Image.NewFromBuffer(imageBytes); - Console.WriteLine($"memory processing {img}"); - // uncomment this line together with the `NObjects` variable in GObject - // Console.WriteLine($"{GObject.NObjects} vips objects known to net-vips"); - } + for (var i = 0; i < 10000; i++) + { + using var img = Image.NewFromBuffer(imageBytes); + Console.WriteLine($"memory processing {img}"); + // uncomment this line together with the `NObjects` variable in GObject + // Console.WriteLine($"{GObject.NObjects} vips objects known to net-vips"); } } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/MutableImage.cs b/samples/NetVips.Samples/Samples/MutableImage.cs index 649a6146..ba0b840d 100644 --- a/samples/NetVips.Samples/Samples/MutableImage.cs +++ b/samples/NetVips.Samples/Samples/MutableImage.cs @@ -1,26 +1,25 @@ -namespace NetVips.Samples +using System; + +namespace NetVips.Samples; + +public class MutableImage : ISample { - using System; + public string Name => "Mutable image"; + public string Category => "Create"; - public class MutableImage : ISample + public void Execute(string[] args) { - public string Name => "Mutable image"; - public string Category => "Create"; - - public void Execute(string[] args) + using var im = Image.Black(500, 500); + using var mutated = im.Mutate(x => { - using var im = Image.Black(500, 500); - using var mutated = im.Mutate(x => + for (var i = 0; i <= 100; i++) { - for (var i = 0; i <= 100; i++) - { - var j = i / 100.0; - x.DrawLine(new[] { 255.0 }, (int)(x.Width * j), 0, 0, (int)(x.Height * (1 - j))); - } - }); - mutated.WriteToFile("mutated.jpg"); + var j = i / 100.0; + x.DrawLine(new[] { 255.0 }, (int)(x.Width * j), 0, 0, (int)(x.Height * (1 - j))); + } + }); + mutated.WriteToFile("mutated.jpg"); - Console.WriteLine("See mutated.jpg"); - } + Console.WriteLine("See mutated.jpg"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/NetworkStream.cs b/samples/NetVips.Samples/Samples/NetworkStream.cs index 60e655e7..50c88f99 100644 --- a/samples/NetVips.Samples/Samples/NetworkStream.cs +++ b/samples/NetVips.Samples/Samples/NetworkStream.cs @@ -1,47 +1,46 @@ -namespace NetVips.Samples +using System; +using System.IO; +using System.Net.Http; + +namespace NetVips.Samples; + +public class NetworkStream : ISample { - using System; - using System.IO; - using System.Net.Http; + public string Name => "Network stream"; + public string Category => "Streaming"; - public class NetworkStream : ISample - { - public string Name => "Network stream"; - public string Category => "Streaming"; + //public const string Uri = "https://github.com/weserv/images/raw/5.x/test/api/fixtures/alpha-layer-2-ink.jpg"; // JPG + public const string Uri = "https://github.com/weserv/images/raw/5.x/test/api/fixtures/alpha-layer-1-fill.png"; // PNG + //public const string Uri = "https://github.com/weserv/images/raw/5.x/test/api/fixtures/dancing_banana2.lossless.webp"; // WebP + //public const string Uri = "https://github.com/weserv/images/raw/5.x/test/api/fixtures/dancing-banana.gif"; // GIF + //public const string Uri = "https://github.com/weserv/images/raw/5.x/test/api/fixtures/tv-test-pattern-146649.svg"; // SVG + //public const string Uri = "https://github.com/weserv/images/raw/5.x/test/api/fixtures/winter_1440x960.heic"; // HEIC + //public const string Uri = "https://github.com/weserv/images/raw/5.x/test/api/fixtures/84y2.hdr"; // HDR - //public const string Uri = "https://github.com/weserv/images/raw/5.x/test/api/fixtures/alpha-layer-2-ink.jpg"; // JPG - public const string Uri = "https://github.com/weserv/images/raw/5.x/test/api/fixtures/alpha-layer-1-fill.png"; // PNG - //public const string Uri = "https://github.com/weserv/images/raw/5.x/test/api/fixtures/dancing_banana2.lossless.webp"; // WebP - //public const string Uri = "https://github.com/weserv/images/raw/5.x/test/api/fixtures/dancing-banana.gif"; // GIF - //public const string Uri = "https://github.com/weserv/images/raw/5.x/test/api/fixtures/tv-test-pattern-146649.svg"; // SVG - //public const string Uri = "https://github.com/weserv/images/raw/5.x/test/api/fixtures/winter_1440x960.heic"; // HEIC - //public const string Uri = "https://github.com/weserv/images/raw/5.x/test/api/fixtures/84y2.hdr"; // HDR + // -1 to test https://github.com/kleisauke/net-vips/issues/101 + public const int BufferSize = 4096 - 1; - // -1 to test https://github.com/kleisauke/net-vips/issues/101 - public const int BufferSize = 4096 - 1; + public async void Execute(string[] args) + { + using var client = new HttpClient(); + await using var stream = await client.GetStreamAsync(Uri); - public async void Execute(string[] args) + using var source = new SourceCustom(); + source.OnRead += (buffer, length) => { - using var client = new HttpClient(); - await using var stream = await client.GetStreamAsync(Uri); - - using var source = new SourceCustom(); - source.OnRead += (buffer, length) => - { - Console.WriteLine($"-> {length} bytes"); - var bytesRead = stream.Read(buffer, 0, length > BufferSize ? BufferSize : length); - Console.WriteLine($"<- {bytesRead} bytes"); - return bytesRead; - }; - - //using var image = Image.NewFromStream(stream, access: Enums.Access.Sequential); - using var image = Image.NewFromSource(source, access: Enums.Access.Sequential); - Console.WriteLine(image.ToString()); - - await using var output = File.OpenWrite("stream-network.jpg"); - image.WriteToStream(output, ".jpg"); - - Console.WriteLine("See stream-network.jpg"); - } + Console.WriteLine($"-> {length} bytes"); + var bytesRead = stream.Read(buffer, 0, length > BufferSize ? BufferSize : length); + Console.WriteLine($"<- {bytesRead} bytes"); + return bytesRead; + }; + + //using var image = Image.NewFromStream(stream, access: Enums.Access.Sequential); + using var image = Image.NewFromSource(source, access: Enums.Access.Sequential); + Console.WriteLine(image.ToString()); + + await using var output = File.OpenWrite("stream-network.jpg"); + image.WriteToStream(output, ".jpg"); + + Console.WriteLine("See stream-network.jpg"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/NewFromMemoryRef.cs b/samples/NetVips.Samples/Samples/NewFromMemoryRef.cs index a8a2363c..e5a25a2a 100644 --- a/samples/NetVips.Samples/Samples/NewFromMemoryRef.cs +++ b/samples/NetVips.Samples/Samples/NewFromMemoryRef.cs @@ -1,36 +1,35 @@ -namespace NetVips.Samples +using System; +using System.Linq; + +namespace NetVips.Samples; + +/// +/// See: https://github.com/libvips/pyvips/pull/104#issuecomment-554632653 +/// +public class NewFromMemoryRef : ISample { - using System; - using System.Linq; + public string Name => "NewFromMemory reference test"; + public string Category => "Internal"; - /// - /// See: https://github.com/libvips/pyvips/pull/104#issuecomment-554632653 - /// - public class NewFromMemoryRef : ISample + public void Execute(string[] args) { - public string Name => "NewFromMemory reference test"; - public string Category => "Internal"; - - public void Execute(string[] args) - { - Cache.Max = 0; + Cache.Max = 0; - using var a = Image.NewFromMemory(Enumerable.Repeat((byte)255, 200).ToArray(), - 20, 10, 1, Enums.BandFormat.Uchar); - using var b = a / 2; + using var a = Image.NewFromMemory(Enumerable.Repeat((byte)255, 200).ToArray(), + 20, 10, 1, Enums.BandFormat.Uchar); + using var b = a / 2; - Console.WriteLine($"Reference count b: {b.RefCount}"); + Console.WriteLine($"Reference count b: {b.RefCount}"); - var average = b.Avg(); + var average = b.Avg(); - Console.WriteLine($"Before GC: {average}"); + Console.WriteLine($"Before GC: {average}"); - GC.Collect(); - GC.WaitForPendingFinalizers(); + GC.Collect(); + GC.WaitForPendingFinalizers(); - average = b.Avg(); + average = b.Avg(); - Console.WriteLine($"After GC: {average}"); - } + Console.WriteLine($"After GC: {average}"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/NewFromStreamRef.cs b/samples/NetVips.Samples/Samples/NewFromStreamRef.cs index b5f1cb02..aa414d7f 100644 --- a/samples/NetVips.Samples/Samples/NewFromStreamRef.cs +++ b/samples/NetVips.Samples/Samples/NewFromStreamRef.cs @@ -1,38 +1,37 @@ -namespace NetVips.Samples -{ - using System; - using System.IO; - /*using System.Threading.Tasks;*/ +using System; +using System.IO; +//using System.Threading.Tasks; - public class NewFromStreamRef : ISample - { - public string Name => "NewFromStream reference test"; - public string Category => "Internal"; +namespace NetVips.Samples; - public const string Filename = "images/equus_quagga.jpg"; +public class NewFromStreamRef : ISample +{ + public string Name => "NewFromStream reference test"; + public string Category => "Internal"; - public void Execute(string[] args) - { - Cache.Max = 0; + public const string Filename = "images/equus_quagga.jpg"; + + public void Execute(string[] args) + { + Cache.Max = 0; - /*Parallel.For(0, 1000, new ParallelOptions { MaxDegreeOfParallelism = NetVips.Concurrency }, - i => - {*/ - using var stream = File.OpenRead(Filename); - var image = Image.NewFromStream(stream, access: Enums.Access.Sequential); + /*Parallel.For(0, 1000, new ParallelOptions { MaxDegreeOfParallelism = NetVips.Concurrency }, + i => + {*/ + using var stream = File.OpenRead(Filename); + var image = Image.NewFromStream(stream, access: Enums.Access.Sequential); - using var mutated = image.Mutate(mutable => mutable.Set(GValue.GStrType, "exif-ifd0-XPComment", "This is a test")); + using var mutated = image.Mutate(mutable => mutable.Set(GValue.GStrType, "exif-ifd0-XPComment", "This is a test")); - Console.WriteLine($"Reference count image: {image.RefCount}"); + Console.WriteLine($"Reference count image: {image.RefCount}"); - // Test to ensure {Read,Seek}Delegate doesn't get disposed - image.Dispose(); + // Test to ensure {Read,Seek}Delegate doesn't get disposed + image.Dispose(); - Console.WriteLine($"Reference count mutated: {mutated.RefCount}"); + Console.WriteLine($"Reference count mutated: {mutated.RefCount}"); - var average = mutated.Avg(); - Console.WriteLine($"Average: {average}"); - /*});*/ - } + var average = mutated.Avg(); + Console.WriteLine($"Average: {average}"); + /*});*/ } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/OnePointMosaic.cs b/samples/NetVips.Samples/Samples/OnePointMosaic.cs index 0bb820c0..41cc056d 100644 --- a/samples/NetVips.Samples/Samples/OnePointMosaic.cs +++ b/samples/NetVips.Samples/Samples/OnePointMosaic.cs @@ -1,97 +1,96 @@ -namespace NetVips.Samples +using System; +using System.Collections.Generic; + +namespace NetVips.Samples; + +/// +/// From: https://github.com/libvips/nip2/tree/master/share/nip2/data/examples/1_point_mosaic +/// +public class OnePointMosaic : ISample { - using System; - using System.Collections.Generic; + public string Name => "1 Point Mosaic"; + public string Category => "Mosaicing"; - /// - /// From: https://github.com/libvips/nip2/tree/master/share/nip2/data/examples/1_point_mosaic - /// - public class OnePointMosaic : ISample + public struct Point { - public string Name => "1 Point Mosaic"; - public string Category => "Mosaicing"; + public int X, Y; - public struct Point + public Point(int x, int y) { - public int X, Y; - - public Point(int x, int y) - { - X = x; - Y = y; - } + X = x; + Y = y; } + } - public List Images = new() - { - "images/cd1.1.jpg", - "images/cd1.2.jpg", - "images/cd2.1.jpg", - "images/cd2.2.jpg", - "images/cd3.1.jpg", - "images/cd3.2.jpg", - "images/cd4.1.jpg", - "images/cd4.2.jpg" - }; + public List Images = new() + { + "images/cd1.1.jpg", + "images/cd1.2.jpg", + "images/cd2.1.jpg", + "images/cd2.2.jpg", + "images/cd3.1.jpg", + "images/cd3.2.jpg", + "images/cd4.1.jpg", + "images/cd4.2.jpg" + }; - public List HorizontalMarks = new() - { - new Point(489, 140), - new Point(66, 141), - new Point(453, 40), - new Point(15, 43), - new Point(500, 122), - new Point(65, 121), - new Point(495, 58), - new Point(40, 57) - }; + public List HorizontalMarks = new() + { + new Point(489, 140), + new Point(66, 141), + new Point(453, 40), + new Point(15, 43), + new Point(500, 122), + new Point(65, 121), + new Point(495, 58), + new Point(40, 57) + }; - public List VerticalMarks = new() - { - new Point(364, 346), - new Point(388, 44), - new Point(385, 629), - new Point(384, 17), - new Point(503, 959), - new Point(527, 42) - }; + public List VerticalMarks = new() + { + new Point(364, 346), + new Point(388, 44), + new Point(385, 629), + new Point(384, 17), + new Point(503, 959), + new Point(527, 42) + }; - public void Execute(string[] args) + public void Execute(string[] args) + { + Image mosaicedImage = null; + for (var i = 0; i < Images.Count; i += 2) { - Image mosaicedImage = null; - for (var i = 0; i < Images.Count; i += 2) + using var image = Image.NewFromFile(Images[i]); + using var secondaryImage = Image.NewFromFile(Images[i + 1]); + + if (mosaicedImage == null) + { + mosaicedImage = image.Mosaic(secondaryImage, Enums.Direction.Horizontal, + HorizontalMarks[i].X, HorizontalMarks[i].Y, + HorizontalMarks[i + 1].X, HorizontalMarks[i + 1].Y); + } + else { - using var image = Image.NewFromFile(Images[i]); - using var secondaryImage = Image.NewFromFile(Images[i + 1]); + using var horizontalPart = image.Mosaic(secondaryImage, Enums.Direction.Horizontal, + HorizontalMarks[i].X, HorizontalMarks[i].Y, + HorizontalMarks[i + 1].X, HorizontalMarks[i + 1].Y); - if (mosaicedImage == null) + using (mosaicedImage) { - mosaicedImage = image.Mosaic(secondaryImage, Enums.Direction.Horizontal, - HorizontalMarks[i].X, HorizontalMarks[i].Y, - HorizontalMarks[i + 1].X, HorizontalMarks[i + 1].Y); - } - else - { - using var horizontalPart = image.Mosaic(secondaryImage, Enums.Direction.Horizontal, - HorizontalMarks[i].X, HorizontalMarks[i].Y, - HorizontalMarks[i + 1].X, HorizontalMarks[i + 1].Y); - - using (mosaicedImage) - { - mosaicedImage = mosaicedImage.Mosaic(horizontalPart, Enums.Direction.Vertical, - VerticalMarks[i - 2].X, VerticalMarks[i - 2].Y, - VerticalMarks[i - 2 + 1].X, VerticalMarks[i - 2 + 1].Y); - } + mosaicedImage = mosaicedImage.Mosaic(horizontalPart, Enums.Direction.Vertical, + VerticalMarks[i - 2].X, VerticalMarks[i - 2].Y, + VerticalMarks[i - 2 + 1].X, VerticalMarks[i - 2 + 1].Y); } } + } - using (mosaicedImage) - { - using var balanced = mosaicedImage.Globalbalance(); - balanced.WriteToFile("1-pt-mosaic.jpg"); - } - - Console.WriteLine("See 1-pt-mosaic.jpg"); + using (mosaicedImage) + { + using var balanced = mosaicedImage.Globalbalance(); + balanced.WriteToFile("1-pt-mosaic.jpg"); } + + Console.WriteLine("See 1-pt-mosaic.jpg"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/OperationBlock.cs b/samples/NetVips.Samples/Samples/OperationBlock.cs index 531fb53d..3b67eb34 100644 --- a/samples/NetVips.Samples/Samples/OperationBlock.cs +++ b/samples/NetVips.Samples/Samples/OperationBlock.cs @@ -1,38 +1,37 @@ -namespace NetVips.Samples -{ - using System; +using System; - public class OperationBlock : ISample - { - public string Name => "Operation block"; - public string Category => "Utils"; +namespace NetVips.Samples; - public const string JpegFilename = "images/lichtenstein.jpg"; - public const string PngFilename = "images/PNG_transparency_demonstration_1.png"; +public class OperationBlock : ISample +{ + public string Name => "Operation block"; + public string Category => "Utils"; - public void Execute(string[] args) - { - // Block all load operations, except JPEG - Operation.Block("VipsForeignLoad", true); - Operation.Block("VipsForeignLoadJpeg", false); + public const string JpegFilename = "images/lichtenstein.jpg"; + public const string PngFilename = "images/PNG_transparency_demonstration_1.png"; - // JPEG images should work - using var image = Image.NewFromFile(JpegFilename, access: Enums.Access.Sequential); - Console.WriteLine($"JPEG average: {image.Avg()}"); + public void Execute(string[] args) + { + // Block all load operations, except JPEG + Operation.Block("VipsForeignLoad", true); + Operation.Block("VipsForeignLoadJpeg", false); - // But PNG images should fail - try - { - using var image2 = Image.NewFromFile(PngFilename, access: Enums.Access.Sequential); - Console.WriteLine($"PNG average: {image2.Avg()}"); - } - catch (VipsException exception) - { - Console.WriteLine(exception.Message); - } + // JPEG images should work + using var image = Image.NewFromFile(JpegFilename, access: Enums.Access.Sequential); + Console.WriteLine($"JPEG average: {image.Avg()}"); - // Re-enable all loaders - Operation.Block("VipsForeignLoad", false); + // But PNG images should fail + try + { + using var image2 = Image.NewFromFile(PngFilename, access: Enums.Access.Sequential); + Console.WriteLine($"PNG average: {image2.Avg()}"); + } + catch (VipsException exception) + { + Console.WriteLine(exception.Message); } + + // Re-enable all loaders + Operation.Block("VipsForeignLoad", false); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/OperationRef.cs b/samples/NetVips.Samples/Samples/OperationRef.cs index 000d3c53..202f0806 100644 --- a/samples/NetVips.Samples/Samples/OperationRef.cs +++ b/samples/NetVips.Samples/Samples/OperationRef.cs @@ -1,60 +1,59 @@ -namespace NetVips.Samples +using System; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace NetVips.Samples; + +/// +/// See: https://github.com/kleisauke/net-vips/issues/53 +/// +public class OperationRef : ISample { - using System; - using System.IO; - using System.Threading; - using System.Threading.Tasks; - using System.Diagnostics; - - /// - /// See: https://github.com/kleisauke/net-vips/issues/53 - /// - public class OperationRef : ISample + public string Name => "Operation reference test"; + public string Category => "Internal"; + + public const string Filename = "images/lichtenstein.jpg"; + + public void Execute(string[] args) { - public string Name => "Operation reference test"; - public string Category => "Internal"; + Cache.Max = 0; - public const string Filename = "images/lichtenstein.jpg"; + using var fileStream = File.OpenRead(Filename); + using var image = Image.NewFromStream(fileStream); - public void Execute(string[] args) + for (var i = 0; i < 1000; i++) { - Cache.Max = 0; - - using var fileStream = File.OpenRead(Filename); - using var image = Image.NewFromStream(fileStream); + using var crop = image.Crop(0, 0, 256, 256); + var _ = crop.Avg(); - for (var i = 0; i < 1000; i++) - { - using var crop = image.Crop(0, 0, 256, 256); - var _ = crop.Avg(); + Console.WriteLine($"reference count: {image.RefCount}"); - Console.WriteLine($"reference count: {image.RefCount}"); + // RefCount should not increase (i.e. operation should be freed) + Debug.Assert(image.RefCount == 2u); + } - // RefCount should not increase (i.e. operation should be freed) - Debug.Assert(image.RefCount == 2u); - } + var count = 0; + var locker = new object(); - var count = 0; - var locker = new object(); + Parallel.For(0, 1000, new ParallelOptions { MaxDegreeOfParallelism = NetVips.Concurrency }, + i => + { + Interlocked.Increment(ref count); - Parallel.For(0, 1000, new ParallelOptions { MaxDegreeOfParallelism = NetVips.Concurrency }, - i => + using var crop = image.Crop(0, 0, 256, 256); + lock (locker) { - Interlocked.Increment(ref count); + var _ = crop.Avg(); - using var crop = image.Crop(0, 0, 256, 256); - lock (locker) - { - var _ = crop.Avg(); + Console.WriteLine($"reference count: {image.RefCount} with {count} active threads"); - Console.WriteLine($"reference count: {image.RefCount} with {count} active threads"); + // RefCount -1 must be lower than or equal to the number of active threads + Debug.Assert(image.RefCount - 1 <= (uint)count); + } - // RefCount -1 must be lower than or equal to the number of active threads - Debug.Assert(image.RefCount - 1 <= (uint)count); - } - - Interlocked.Decrement(ref count); - }); - } + Interlocked.Decrement(ref count); + }); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/PostClose.cs b/samples/NetVips.Samples/Samples/PostClose.cs index d737e73f..4825af45 100644 --- a/samples/NetVips.Samples/Samples/PostClose.cs +++ b/samples/NetVips.Samples/Samples/PostClose.cs @@ -1,38 +1,37 @@ -namespace NetVips.Samples -{ - using System; +using System; - public class PostClose : ISample - { - public string Name => "Post close"; - public string Category => "Other"; +namespace NetVips.Samples; - public const string Filename = "images/sample2.v"; +public class PostClose : ISample +{ + public string Name => "Post close"; + public string Category => "Other"; - public void OnPostClose() - { - Console.WriteLine("Post close!"); - } + public const string Filename = "images/sample2.v"; - public void Execute(string[] args) - { - // Avoid reusing the image after subsequent use - Cache.Max = 0; + public void OnPostClose() + { + Console.WriteLine("Post close!"); + } + + public void Execute(string[] args) + { + // Avoid reusing the image after subsequent use + Cache.Max = 0; - var action = OnPostClose; + var action = OnPostClose; - var im = Image.NewFromFile(Filename, access: Enums.Access.Sequential); - im.OnPostClose += action; + var im = Image.NewFromFile(Filename, access: Enums.Access.Sequential); + im.OnPostClose += action; - // This will call OnPostClose - im.Dispose(); + // This will call OnPostClose + im.Dispose(); - im = Image.NewFromFile(Filename, access: Enums.Access.Sequential); - im.OnPostClose += action; - im.OnPostClose -= action; + im = Image.NewFromFile(Filename, access: Enums.Access.Sequential); + im.OnPostClose += action; + im.OnPostClose -= action; - // This will not call OnPostClose - im.Dispose(); - } + // This will not call OnPostClose + im.Dispose(); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/Progress.cs b/samples/NetVips.Samples/Samples/Progress.cs index 22420f1d..80af1a31 100644 --- a/samples/NetVips.Samples/Samples/Progress.cs +++ b/samples/NetVips.Samples/Samples/Progress.cs @@ -1,55 +1,54 @@ -namespace NetVips.Samples +using System; + +namespace NetVips.Samples; + +public class Progress : ISample { - using System; + public string Name => "Progress reporting"; + public string Category => "Other"; + + public const int TileSize = 50; + public const string Filename = "images/sample2.v"; + + public void Execute(string[] args) + { + // Build test image + using var im = Image.NewFromFile(Filename, access: Enums.Access.Sequential); + using var test = im.Replicate(TileSize, TileSize); + + // Enable progress reporting + test.SetProgress(true); + + // Connect signals + test.SignalConnect(Enums.Signals.PreEval, PreEvalHandler); + test.SignalConnect(Enums.Signals.Eval, EvalHandler); + test.SignalConnect(Enums.Signals.PostEval, PostEvalHandler); + + _ = test.Avg(); + } + + private void ProgressPrint(Enums.Signals signal, VipsProgress progress) + { + Console.WriteLine($"{signal}:"); + Console.WriteLine($" Run = : {progress.Run}"); + Console.WriteLine($" Eta = : {progress.Eta}"); + Console.WriteLine($" TPels = : {progress.TPels}"); + Console.WriteLine($" NPels = : {progress.NPels}"); + Console.WriteLine($" Percent = : {progress.Percent}"); + } + + private void PreEvalHandler(Image image, VipsProgress progress) + { + ProgressPrint(Enums.Signals.PreEval, progress); + } + + private void EvalHandler(Image image, VipsProgress progress) + { + ProgressPrint(Enums.Signals.Eval, progress); + } - public class Progress : ISample + private void PostEvalHandler(Image image, VipsProgress progress) { - public string Name => "Progress reporting"; - public string Category => "Other"; - - public const int TileSize = 50; - public const string Filename = "images/sample2.v"; - - public void Execute(string[] args) - { - // Build test image - using var im = Image.NewFromFile(Filename, access: Enums.Access.Sequential); - using var test = im.Replicate(TileSize, TileSize); - - // Enable progress reporting - test.SetProgress(true); - - // Connect signals - test.SignalConnect(Enums.Signals.PreEval, PreEvalHandler); - test.SignalConnect(Enums.Signals.Eval, EvalHandler); - test.SignalConnect(Enums.Signals.PostEval, PostEvalHandler); - - _ = test.Avg(); - } - - private void ProgressPrint(Enums.Signals signal, VipsProgress progress) - { - Console.WriteLine($"{signal}:"); - Console.WriteLine($" Run = : {progress.Run}"); - Console.WriteLine($" Eta = : {progress.Eta}"); - Console.WriteLine($" TPels = : {progress.TPels}"); - Console.WriteLine($" NPels = : {progress.NPels}"); - Console.WriteLine($" Percent = : {progress.Percent}"); - } - - private void PreEvalHandler(Image image, VipsProgress progress) - { - ProgressPrint(Enums.Signals.PreEval, progress); - } - - private void EvalHandler(Image image, VipsProgress progress) - { - ProgressPrint(Enums.Signals.Eval, progress); - } - - private void PostEvalHandler(Image image, VipsProgress progress) - { - ProgressPrint(Enums.Signals.PostEval, progress); - } + ProgressPrint(Enums.Signals.PostEval, progress); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/RandomCropper.cs b/samples/NetVips.Samples/Samples/RandomCropper.cs index 9c38800b..c4bdc17f 100644 --- a/samples/NetVips.Samples/Samples/RandomCropper.cs +++ b/samples/NetVips.Samples/Samples/RandomCropper.cs @@ -1,45 +1,44 @@ -namespace NetVips.Samples -{ - using System; - using System.IO; - using System.Threading.Tasks; - - /// - /// See: https://github.com/kleisauke/net-vips/issues/58 - /// - public class RandomCropper : ISample - { - public string Name => "Random cropper"; - public string Category => "Internal"; +using System; +using System.IO; +using System.Threading.Tasks; - public const int TileSize = 256; +namespace NetVips.Samples; - public const string Filename = "images/equus_quagga.jpg"; +/// +/// See: https://github.com/kleisauke/net-vips/issues/58 +/// +public class RandomCropper : ISample +{ + public string Name => "Random cropper"; + public string Category => "Internal"; - public static readonly Random Rnd = new(); + public const int TileSize = 256; - public Image RandomCrop(Image image, int tileSize) - { - var x = Rnd.Next(0, image.Width); - var y = Rnd.Next(0, image.Height); + public const string Filename = "images/equus_quagga.jpg"; - var width = Math.Min(tileSize, image.Width - x); - var height = Math.Min(tileSize, image.Height - y); + public static readonly Random Rnd = new(); - return image.Crop(x, y, width, height); - } + public Image RandomCrop(Image image, int tileSize) + { + var x = Rnd.Next(0, image.Width); + var y = Rnd.Next(0, image.Height); - public void Execute(string[] args) - { - using var fileStream = File.OpenRead(Filename); - using var image = Image.NewFromStream(fileStream); + var width = Math.Min(tileSize, image.Width - x); + var height = Math.Min(tileSize, image.Height - y); - Parallel.For(0, 1000, new ParallelOptions {MaxDegreeOfParallelism = NetVips.Concurrency}, - i => - { - using var crop = RandomCrop(image, TileSize); - crop.WriteToFile($"x_{i}.png"); - }); - } + return image.Crop(x, y, width, height); + } + + public void Execute(string[] args) + { + using var fileStream = File.OpenRead(Filename); + using var image = Image.NewFromStream(fileStream); + + Parallel.For(0, 1000, new ParallelOptions {MaxDegreeOfParallelism = NetVips.Concurrency}, + i => + { + using var crop = RandomCrop(image, TileSize); + crop.WriteToFile($"x_{i}.png"); + }); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/ShapeCropping.cs b/samples/NetVips.Samples/Samples/ShapeCropping.cs index 2ae8db3d..1e8ce139 100644 --- a/samples/NetVips.Samples/Samples/ShapeCropping.cs +++ b/samples/NetVips.Samples/Samples/ShapeCropping.cs @@ -1,317 +1,316 @@ -namespace NetVips.Samples -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; - /// - /// From: https://github.com/weserv/images/blob/3.x/src/Manipulators/Shape.php - /// - public class ShapeCropping : ISample - { - public string Name => "Shape cropping"; - public string Category => "Conversion"; +namespace NetVips.Samples; - public const string Filename = "images/lichtenstein.jpg"; +/// +/// From: https://github.com/weserv/images/blob/3.x/src/Manipulators/Shape.php +/// +public class ShapeCropping : ISample +{ + public string Name => "Shape cropping"; + public string Category => "Conversion"; - public enum Shape - { - Circle, - Ellipse, - Triangle, - Triangle180, - Pentagon, - Pentagon180, - Hexagon, - Square, - Star, - Heart - } + public const string Filename = "images/lichtenstein.jpg"; - public const Shape CurrentShape = Shape.Circle; + public enum Shape + { + Circle, + Ellipse, + Triangle, + Triangle180, + Pentagon, + Pentagon180, + Hexagon, + Square, + Star, + Heart + } - public const bool Crop = true; + public const Shape CurrentShape = Shape.Circle; - public void Execute(string[] args) - { - using var image = Image.NewFromFile(Filename, access: Enums.Access.Sequential); - var width = image.Width; - var height = image.Height; + public const bool Crop = true; + + public void Execute(string[] args) + { + using var image = Image.NewFromFile(Filename, access: Enums.Access.Sequential); + var width = image.Width; + var height = image.Height; - var path = GetSvgShape(width, height, CurrentShape, out var xMin, out var yMin, out var maskWidth, - out var maskHeight); + var path = GetSvgShape(width, height, CurrentShape, out var xMin, out var yMin, out var maskWidth, + out var maskHeight); - var preserveAspectRatio = CurrentShape.Equals(Shape.Ellipse) ? "none" : "xMidYMid meet"; - var svg = new StringBuilder(""); - svg.Append($""); - svg.Append(path); - svg.Append(""); + var preserveAspectRatio = CurrentShape.Equals(Shape.Ellipse) ? "none" : "xMidYMid meet"; + var svg = new StringBuilder(""); + svg.Append($""); + svg.Append(path); + svg.Append(""); - using var mask = Image.NewFromBuffer(svg.ToString(), access: Enums.Access.Sequential); + using var mask = Image.NewFromBuffer(svg.ToString(), access: Enums.Access.Sequential); - // Cutout via dest-in - var composite = image.Composite(mask, Enums.BlendMode.DestIn); + // Cutout via dest-in + var composite = image.Composite(mask, Enums.BlendMode.DestIn); - // Crop the image to the mask dimensions - if (Crop && !CurrentShape.Equals(Shape.Ellipse)) + // Crop the image to the mask dimensions + if (Crop && !CurrentShape.Equals(Shape.Ellipse)) + { + var trim = ResolveShapeTrim(width, height, maskWidth, maskHeight); + var left = trim[0]; + var top = trim[1]; + var trimWidth = trim[2]; + var trimHeight = trim[3]; + + // Crop if the trim dimensions is less than the image dimensions + if (trimWidth < width || trimHeight < height) { - var trim = ResolveShapeTrim(width, height, maskWidth, maskHeight); - var left = trim[0]; - var top = trim[1]; - var trimWidth = trim[2]; - var trimHeight = trim[3]; - - // Crop if the trim dimensions is less than the image dimensions - if (trimWidth < width || trimHeight < height) + using (composite) { - using (composite) - { - composite = composite.Crop(left, top, trimWidth, trimHeight); - } + composite = composite.Crop(left, top, trimWidth, trimHeight); } } - - using (composite) - { - composite.WriteToFile("shape.png"); - } - - Console.WriteLine("See shape.png"); } - /// - /// Get the SVG shape - /// - /// Image width - /// Image height - /// Shape in which the image is to be cropped - /// Left edge of mask - /// Top edge of mask - /// Mask width - /// Mask height - /// SVG path - public string GetSvgShape(int width, int height, Shape shape, out int xMin, out int yMin, out int maskWidth, - out int maskHeight) + using (composite) { - var min = Math.Min(width, height); - var outerRadius = min / 2.0; - var midX = width / 2; - var midY = height / 2; + composite.WriteToFile("shape.png"); + } - // 'inner' radius of the polygon/star - var innerRadius = outerRadius; + Console.WriteLine("See shape.png"); + } - // Initial angle (clockwise). By default, stars and polygons are 'pointing' up. - var initialAngle = 0.0; + /// + /// Get the SVG shape + /// + /// Image width + /// Image height + /// Shape in which the image is to be cropped + /// Left edge of mask + /// Top edge of mask + /// Mask width + /// Mask height + /// SVG path + public string GetSvgShape(int width, int height, Shape shape, out int xMin, out int yMin, out int maskWidth, + out int maskHeight) + { + var min = Math.Min(width, height); + var outerRadius = min / 2.0; + var midX = width / 2; + var midY = height / 2; - // Number of points (or number of sides for polygons) - int points; + // 'inner' radius of the polygon/star + var innerRadius = outerRadius; - switch (shape) - { - case Shape.Hexagon: - // Hexagon - points = 6; - break; - case Shape.Pentagon: - // Pentagon - points = 5; - break; - case Shape.Pentagon180: - // Pentagon tilted upside down - points = 5; - initialAngle = Math.PI; - break; - case Shape.Star: - // 5 point star - points = 5 * 2; - innerRadius *= .382; - break; - case Shape.Square: - // Square tilted 45 degrees - points = 4; - break; - case Shape.Triangle: - // Triangle - points = 3; - break; - case Shape.Triangle180: - // Triangle upside down - points = 3; - initialAngle = Math.PI; - break; - case Shape.Circle: - xMin = (int)(midX - outerRadius); - yMin = (int)(midY - outerRadius); - maskWidth = min; - maskHeight = min; - - // Circle - return $""; - case Shape.Ellipse: - xMin = 0; - yMin = 0; - maskWidth = width; - maskHeight = height; - - // Ellipse - return $""; - case Shape.Heart: - // Heart - return GetSvgHeart(outerRadius, outerRadius, out xMin, out yMin, out maskWidth, out maskHeight); - default: - throw new ArgumentOutOfRangeException(nameof(shape), shape, null); - } + // Initial angle (clockwise). By default, stars and polygons are 'pointing' up. + var initialAngle = 0.0; - return GetSvgMask(midX, midY, points, outerRadius, innerRadius, initialAngle, out xMin, out yMin, - out maskWidth, out maskHeight); - } + // Number of points (or number of sides for polygons) + int points; - /// - /// Formula from http://mathworld.wolfram.com/HeartCurve.html - /// - /// Image width / 2 - /// Image height / 2 - /// Left edge of mask - /// Top edge of mask - /// Mask width - /// Mask height - /// SVG path - public string GetSvgHeart(double midX, double midY, out int xMin, out int yMin, out int maskWidth, - out int maskHeight) + switch (shape) { - var path = new StringBuilder(); - var xArr = new List(); - var yArr = new List(); + case Shape.Hexagon: + // Hexagon + points = 6; + break; + case Shape.Pentagon: + // Pentagon + points = 5; + break; + case Shape.Pentagon180: + // Pentagon tilted upside down + points = 5; + initialAngle = Math.PI; + break; + case Shape.Star: + // 5 point star + points = 5 * 2; + innerRadius *= .382; + break; + case Shape.Square: + // Square tilted 45 degrees + points = 4; + break; + case Shape.Triangle: + // Triangle + points = 3; + break; + case Shape.Triangle180: + // Triangle upside down + points = 3; + initialAngle = Math.PI; + break; + case Shape.Circle: + xMin = (int)(midX - outerRadius); + yMin = (int)(midY - outerRadius); + maskWidth = min; + maskHeight = min; + + // Circle + return $""; + case Shape.Ellipse: + xMin = 0; + yMin = 0; + maskWidth = width; + maskHeight = height; + + // Ellipse + return $""; + case Shape.Heart: + // Heart + return GetSvgHeart(outerRadius, outerRadius, out xMin, out yMin, out maskWidth, out maskHeight); + default: + throw new ArgumentOutOfRangeException(nameof(shape), shape, null); + } - for (var t = -Math.PI; t <= Math.PI; t += 0.02) - { - var xPt = 16 * Math.Pow(Math.Sin(t), 3); - var yPt = 13 * Math.Cos(t) - 5 * Math.Cos(2 * t) - 2 * Math.Cos(3 * t) - Math.Cos(4 * t); - - var x = (int)Math.Round(midX + xPt * midX); - var y = (int)Math.Round(midY - yPt * midY); - xArr.Add(x); - yArr.Add(y); - path.Append($"{x} {y} L"); - } + return GetSvgMask(midX, midY, points, outerRadius, innerRadius, initialAngle, out xMin, out yMin, + out maskWidth, out maskHeight); + } - xMin = xArr.Min(); - yMin = yArr.Min(); - maskWidth = xArr.Max() - xMin; - maskHeight = yArr.Max() - yMin; + /// + /// Formula from http://mathworld.wolfram.com/HeartCurve.html + /// + /// Image width / 2 + /// Image height / 2 + /// Left edge of mask + /// Top edge of mask + /// Mask width + /// Mask height + /// SVG path + public string GetSvgHeart(double midX, double midY, out int xMin, out int yMin, out int maskWidth, + out int maskHeight) + { + var path = new StringBuilder(); + var xArr = new List(); + var yArr = new List(); - return $""; + for (var t = -Math.PI; t <= Math.PI; t += 0.02) + { + var xPt = 16 * Math.Pow(Math.Sin(t), 3); + var yPt = 13 * Math.Cos(t) - 5 * Math.Cos(2 * t) - 2 * Math.Cos(3 * t) - Math.Cos(4 * t); + + var x = (int)Math.Round(midX + xPt * midX); + var y = (int)Math.Round(midY - yPt * midY); + xArr.Add(x); + yArr.Add(y); + path.Append($"{x} {y} L"); } - /// - /// Inspired by this JSFiddle: http://jsfiddle.net/tohan/8vwjn4cx/ - /// modified to support SVG paths - /// - /// Image width / 2 - /// Image height / 2 - /// Number of points (or number of sides for polygons) - /// 'outer' radius of the star - /// 'inner' radius of the star (if equal to outerRadius, a polygon is drawn) - /// Initial angle (clockwise). By default, stars and polygons are 'pointing' up. - /// Left edge of mask - /// Top edge of mask - /// Mask width - /// Mask height - /// SVG Path - public string GetSvgMask( - int midX, int midY, int points, double outerRadius, double innerRadius, double initialAngle, - out int xMin, out int yMin, out int maskWidth, out int maskHeight) - { - var path = new StringBuilder(); - var xArr = new List(); - var yArr = new List(); + xMin = xArr.Min(); + yMin = yArr.Min(); + maskWidth = xArr.Max() - xMin; + maskHeight = yArr.Max() - yMin; - for (var i = 0; i <= points; i++) + return $""; + } + + /// + /// Inspired by this JSFiddle: http://jsfiddle.net/tohan/8vwjn4cx/ + /// modified to support SVG paths + /// + /// Image width / 2 + /// Image height / 2 + /// Number of points (or number of sides for polygons) + /// 'outer' radius of the star + /// 'inner' radius of the star (if equal to outerRadius, a polygon is drawn) + /// Initial angle (clockwise). By default, stars and polygons are 'pointing' up. + /// Left edge of mask + /// Top edge of mask + /// Mask width + /// Mask height + /// SVG Path + public string GetSvgMask( + int midX, int midY, int points, double outerRadius, double innerRadius, double initialAngle, + out int xMin, out int yMin, out int maskWidth, out int maskHeight) + { + var path = new StringBuilder(); + var xArr = new List(); + var yArr = new List(); + + for (var i = 0; i <= points; i++) + { + var angle = i * 2 * Math.PI / points - Math.PI / 2 + initialAngle; + var radius = i % 2 == 0 ? outerRadius : innerRadius; + if (i == 0) { - var angle = i * 2 * Math.PI / points - Math.PI / 2 + initialAngle; - var radius = i % 2 == 0 ? outerRadius : innerRadius; - if (i == 0) - { - path.Append('M'); - - // If an odd number of points, add an additional point at the top of the polygon - // -- this will shift the calculated center point of the shape so that the center point - // of the polygon is at x,y (otherwise the center is mis-located) - if (points % 2 == 1) - { - path.Append($"0 {radius} M"); - } - } - else + path.Append('M'); + + // If an odd number of points, add an additional point at the top of the polygon + // -- this will shift the calculated center point of the shape so that the center point + // of the polygon is at x,y (otherwise the center is mis-located) + if (points % 2 == 1) { - path.Append(" L"); + path.Append($"0 {radius} M"); } - - var x = (int)Math.Round(midX + radius * Math.Cos(angle)); - var y = (int)Math.Round(midY + radius * Math.Sin(angle)); - xArr.Add(x); - yArr.Add(y); - path.Append($"{x} {y} L"); + } + else + { + path.Append(" L"); } - xMin = xArr.Min(); - yMin = yArr.Min(); - maskWidth = xArr.Max() - xMin; - maskHeight = yArr.Max() - yMin; - - return $""; + var x = (int)Math.Round(midX + radius * Math.Cos(angle)); + var y = (int)Math.Round(midY + radius * Math.Sin(angle)); + xArr.Add(x); + yArr.Add(y); + path.Append($"{x} {y} L"); } - /// - /// Calculate the area to extract - /// - /// Image width - /// Image height - /// Mask width - /// Mask height - /// - public int[] ResolveShapeTrim(int width, int height, int maskWidth, int maskHeight) - { - var xScale = (double)width / maskWidth; - var yScale = (double)height / maskHeight; - var scale = Math.Min(xScale, yScale); - var trimWidth = maskWidth * scale; - var trimHeight = maskHeight * scale; - var left = (int)Math.Round((width - trimWidth) / 2); - var top = (int)Math.Round((height - trimHeight) / 2); - - return new[] { left, top, (int)Math.Round(trimWidth), (int)Math.Round(trimHeight) }; - } + xMin = xArr.Min(); + yMin = yArr.Min(); + maskWidth = xArr.Max() - xMin; + maskHeight = yArr.Max() - yMin; + + return $""; + } - #region helpers + /// + /// Calculate the area to extract + /// + /// Image width + /// Image height + /// Mask width + /// Mask height + /// + public int[] ResolveShapeTrim(int width, int height, int maskWidth, int maskHeight) + { + var xScale = (double)width / maskWidth; + var yScale = (double)height / maskHeight; + var scale = Math.Min(xScale, yScale); + var trimWidth = maskWidth * scale; + var trimHeight = maskHeight * scale; + var left = (int)Math.Round((width - trimWidth) / 2); + var top = (int)Math.Round((height - trimHeight) / 2); + + return new[] { left, top, (int)Math.Round(trimWidth), (int)Math.Round(trimHeight) }; + } - /// - /// Return the image alpha maximum. Useful for combining alpha bands. scRGB - /// images are 0 - 1 for image data, but the alpha is 0 - 255. - /// - /// The - /// the image alpha maximum - public static int MaximumImageAlpha(Enums.Interpretation interpretation) - { - return Is16Bit(interpretation) ? 65535 : 255; - } + #region helpers - /// - /// Are pixel values in this image 16-bit integer? - /// - /// The - /// if the pixel values in this image are 16-bit; - /// otherwise, - public static bool Is16Bit(Enums.Interpretation interpretation) - { - return interpretation == Enums.Interpretation.Rgb16 || - interpretation == Enums.Interpretation.Grey16; - } + /// + /// Return the image alpha maximum. Useful for combining alpha bands. scRGB + /// images are 0 - 1 for image data, but the alpha is 0 - 255. + /// + /// The + /// the image alpha maximum + public static int MaximumImageAlpha(Enums.Interpretation interpretation) + { + return Is16Bit(interpretation) ? 65535 : 255; + } - #endregion + /// + /// Are pixel values in this image 16-bit integer? + /// + /// The + /// if the pixel values in this image are 16-bit; + /// otherwise, + public static bool Is16Bit(Enums.Interpretation interpretation) + { + return interpretation == Enums.Interpretation.Rgb16 || + interpretation == Enums.Interpretation.Grey16; } + + #endregion } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/Smartcrop.cs b/samples/NetVips.Samples/Samples/Smartcrop.cs index 83dc0609..c61baf12 100644 --- a/samples/NetVips.Samples/Samples/Smartcrop.cs +++ b/samples/NetVips.Samples/Samples/Smartcrop.cs @@ -1,20 +1,19 @@ -namespace NetVips.Samples -{ - using System; +using System; - public class Smartcrop : ISample - { - public string Name => "Smartcrop"; - public string Category => "Conversion"; +namespace NetVips.Samples; - public const string Filename = "images/equus_quagga.jpg"; +public class Smartcrop : ISample +{ + public string Name => "Smartcrop"; + public string Category => "Conversion"; - public void Execute(string[] args) - { - using var image = Image.Thumbnail(Filename, 300, height: 300, crop: Enums.Interesting.Attention); - image.WriteToFile("smartcrop.jpg"); + public const string Filename = "images/equus_quagga.jpg"; + + public void Execute(string[] args) + { + using var image = Image.Thumbnail(Filename, 300, height: 300, crop: Enums.Interesting.Attention); + image.WriteToFile("smartcrop.jpg"); - Console.WriteLine("See smartcrop.jpg"); - } + Console.WriteLine("See smartcrop.jpg"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/Sobel.cs b/samples/NetVips.Samples/Samples/Sobel.cs index 2c22f7b2..04e7ef89 100644 --- a/samples/NetVips.Samples/Samples/Sobel.cs +++ b/samples/NetVips.Samples/Samples/Sobel.cs @@ -1,26 +1,25 @@ -namespace NetVips.Samples -{ - using System; +using System; - public class Sobel : ISample - { - public string Name => "Sobel"; - public string Category => "Edge detection"; +namespace NetVips.Samples; - public const string Filename = "images/lichtenstein.jpg"; +public class Sobel : ISample +{ + public string Name => "Sobel"; + public string Category => "Edge detection"; - public void Execute(string[] args) - { - using var im = Image.NewFromFile(Filename, access: Enums.Access.Sequential); + public const string Filename = "images/lichtenstein.jpg"; + + public void Execute(string[] args) + { + using var im = Image.NewFromFile(Filename, access: Enums.Access.Sequential); - // Optionally, convert to greyscale - //using var mono = im.Colourspace(Enums.Interpretation.Bw); + // Optionally, convert to greyscale + //using var mono = im.Colourspace(Enums.Interpretation.Bw); - // Apply sobel operator - using var sobel = /*mono*/im.Sobel(); - sobel.WriteToFile("sobel.jpg"); + // Apply sobel operator + using var sobel = /*mono*/im.Sobel(); + sobel.WriteToFile("sobel.jpg"); - Console.WriteLine("See sobel.jpg"); - } + Console.WriteLine("See sobel.jpg"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/Stream.cs b/samples/NetVips.Samples/Samples/Stream.cs index 606a6f93..1893f969 100644 --- a/samples/NetVips.Samples/Samples/Stream.cs +++ b/samples/NetVips.Samples/Samples/Stream.cs @@ -1,26 +1,25 @@ -namespace NetVips.Samples -{ - using System; - using System.IO; +using System; +using System.IO; - public class Stream : ISample - { - public string Name => "File stream"; - public string Category => "Streaming"; +namespace NetVips.Samples; - public const string Filename = "images/PNG_transparency_demonstration_1.png"; +public class Stream : ISample +{ + public string Name => "File stream"; + public string Category => "Streaming"; - public void Execute(string[] args) - { - using var input = File.OpenRead(Filename); + public const string Filename = "images/PNG_transparency_demonstration_1.png"; + + public void Execute(string[] args) + { + using var input = File.OpenRead(Filename); - using var image = Image.NewFromStream(input, access: Enums.Access.Sequential); - Console.WriteLine(image.ToString()); + using var image = Image.NewFromStream(input, access: Enums.Access.Sequential); + Console.WriteLine(image.ToString()); - using var output = File.OpenWrite("stream.png"); - image.WriteToStream(output, ".png"); + using var output = File.OpenWrite("stream.png"); + image.WriteToStream(output, ".png"); - Console.WriteLine("See stream.png"); - } + Console.WriteLine("See stream.png"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/Thumbnail.cs b/samples/NetVips.Samples/Samples/Thumbnail.cs index a1517111..c3458e13 100644 --- a/samples/NetVips.Samples/Samples/Thumbnail.cs +++ b/samples/NetVips.Samples/Samples/Thumbnail.cs @@ -1,20 +1,19 @@ -namespace NetVips.Samples -{ - using System; +using System; - public class Thumbnail : ISample - { - public string Name => "Thumbnail"; - public string Category => "Resample"; +namespace NetVips.Samples; - public const string Filename = "images/lichtenstein.jpg"; +public class Thumbnail : ISample +{ + public string Name => "Thumbnail"; + public string Category => "Resample"; - public void Execute(string[] args) - { - using var image = Image.Thumbnail(Filename, 300, height: 300); - image.WriteToFile("thumbnail.jpg"); + public const string Filename = "images/lichtenstein.jpg"; + + public void Execute(string[] args) + { + using var image = Image.Thumbnail(Filename, 300, height: 300); + image.WriteToFile("thumbnail.jpg"); - Console.WriteLine("See thumbnail.jpg"); - } + Console.WriteLine("See thumbnail.jpg"); } } \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/ThumbnailPipeline.cs b/samples/NetVips.Samples/Samples/ThumbnailPipeline.cs index 20548824..198696ef 100644 --- a/samples/NetVips.Samples/Samples/ThumbnailPipeline.cs +++ b/samples/NetVips.Samples/Samples/ThumbnailPipeline.cs @@ -1,225 +1,224 @@ -namespace NetVips.Samples +using System; +using System.IO; + +namespace NetVips.Samples; + +public class ThumbnailPipeline : ISample { - using System; - using System.IO; + public string Name => "Thumbnail (configurable pipeline)"; + public string Category => "Resample"; + + // Source: https://storage.googleapis.com/downloads.webmproject.org/webp/images/dancing-banana.gif + public const string Filename = "images/dancing-banana.gif"; + + // Maximum value for a coordinate. + private const int VipsMaxCoord = 10000000; + + // = 71 megapixels + private const int MaxImageSize = 71000000; + + // Halt processing and raise an error when loading invalid images. + // Set this flag to Enums.FailOn.None if you'd rather apply a "best effort" to decode + // images, even if the data is corrupt or invalid. + // See: CVE-2019-6976 + // https://blog.silentsignal.eu/2019/04/18/drop-by-drop-bleeding-through-libvips/ + private const Enums.FailOn FailOn = Enums.FailOn.Error; + + // The name libvips uses to attach an ICC profile. + private const string VipsMetaIccName = "icc-profile-data"; + + /// + /// Does this loader support multiple pages? + /// + /// The name of the load operation. + /// A bool indicating if this loader support multiple pages. + public bool LoaderSupportPage(string loader) + { + return loader.StartsWith("VipsForeignLoadPdf") || + loader.StartsWith("VipsForeignLoadNsgif") || + loader.StartsWith("VipsForeignLoadTiff") || + loader.StartsWith("VipsForeignLoadWebp") || + loader.StartsWith("VipsForeignLoadHeif") || + loader.StartsWith("VipsForeignLoadMagick"); + } - public class ThumbnailPipeline : ISample +#pragma warning disable CS0162 // Unreachable code detected + public void Execute(string[] args) { - public string Name => "Thumbnail (configurable pipeline)"; - public string Category => "Resample"; - - // Source: https://storage.googleapis.com/downloads.webmproject.org/webp/images/dancing-banana.gif - public const string Filename = "images/dancing-banana.gif"; - - // Maximum value for a coordinate. - private const int VipsMaxCoord = 10000000; - - // = 71 megapixels - private const int MaxImageSize = 71000000; - - // Halt processing and raise an error when loading invalid images. - // Set this flag to Enums.FailOn.None if you'd rather apply a "best effort" to decode - // images, even if the data is corrupt or invalid. - // See: CVE-2019-6976 - // https://blog.silentsignal.eu/2019/04/18/drop-by-drop-bleeding-through-libvips/ - private const Enums.FailOn FailOn = Enums.FailOn.Error; - - // The name libvips uses to attach an ICC profile. - private const string VipsMetaIccName = "icc-profile-data"; - - /// - /// Does this loader support multiple pages? - /// - /// The name of the load operation. - /// A bool indicating if this loader support multiple pages. - public bool LoaderSupportPage(string loader) + // If you set a number to zero (0), it will resize on the other specified axis. + var width = 200; + var height = 0; + + // Enums.Size.Both - for both up and down. + // Enums.Size.Up - only upsize. + // Enums.Size.Down - only downsize. + // Enums.Size.Force - force size, that is, break aspect ratio. + const Enums.Size size = Enums.Size.Both; + + // Just for example. + var buffer = File.ReadAllBytes(Filename); + + // Find the name of the load operation vips will use to load a buffer + // so that we can work out what options to pass to NewFromBuffer(). + var loader = Image.FindLoadBuffer(buffer); + + if (loader == null) { - return loader.StartsWith("VipsForeignLoadPdf") || - loader.StartsWith("VipsForeignLoadNsgif") || - loader.StartsWith("VipsForeignLoadTiff") || - loader.StartsWith("VipsForeignLoadWebp") || - loader.StartsWith("VipsForeignLoadHeif") || - loader.StartsWith("VipsForeignLoadMagick"); + // No known loader is found, stop further processing. + throw new Exception("Invalid or unsupported image format. Is it a valid image?"); } -#pragma warning disable CS0162 // Unreachable code detected - public void Execute(string[] args) + var loadOptions = new VOption { - // If you set a number to zero (0), it will resize on the other specified axis. - var width = 200; - var height = 0; + {"access", Enums.Access.Sequential}, + {"fail_on", FailOn} + }; + var stringOptions = ""; - // Enums.Size.Both - for both up and down. - // Enums.Size.Up - only upsize. - // Enums.Size.Down - only downsize. - // Enums.Size.Force - force size, that is, break aspect ratio. - const Enums.Size size = Enums.Size.Both; + if (LoaderSupportPage(loader)) + { + // -1 means "until the end of the document", handy for animated images. + loadOptions.Add("n", -1); + stringOptions = "[n=-1]"; + } - // Just for example. - var buffer = File.ReadAllBytes(Filename); + int inputWidth; + int inputHeight; + int pageHeight; + bool hasIccProfile; - // Find the name of the load operation vips will use to load a buffer - // so that we can work out what options to pass to NewFromBuffer(). - var loader = Image.FindLoadBuffer(buffer); + Image image = null; + try + { + image = (Image)Operation.Call(loader, loadOptions, buffer); - if (loader == null) - { - // No known loader is found, stop further processing. - throw new Exception("Invalid or unsupported image format. Is it a valid image?"); - } + // Or: + // image = Image.NewFromBuffer(buffer, kwargs: loadOptions); + // (but the loader is already found, so the above will be a little faster). - var loadOptions = new VOption - { - {"access", Enums.Access.Sequential}, - {"fail_on", FailOn} - }; - var stringOptions = ""; + inputWidth = image.Width; + inputHeight = image.Height; - if (LoaderSupportPage(loader)) + // Use 64-bit unsigned type, to handle PNG decompression bombs. + if ((ulong)(inputWidth * inputHeight) > MaxImageSize) { - // -1 means "until the end of the document", handy for animated images. - loadOptions.Add("n", -1); - stringOptions = "[n=-1]"; + throw new Exception( + "Image is too large for processing. Width x height should be less than 71 megapixels."); } - int inputWidth; - int inputHeight; - int pageHeight; - bool hasIccProfile; + pageHeight = image.PageHeight; + hasIccProfile = image.Contains(VipsMetaIccName); + } + catch (VipsException e) + { + throw new Exception("Image has a corrupt header.", e); + } + finally + { + // We're done with the image; dispose early + image?.Dispose(); + } - Image image = null; - try - { - image = (Image)Operation.Call(loader, loadOptions, buffer); - // Or: - // image = Image.NewFromBuffer(buffer, kwargs: loadOptions); - // (but the loader is already found, so the above will be a little faster). + string importProfile = null; + string exportProfile = null; + Enums.Intent? intent = null; - inputWidth = image.Width; - inputHeight = image.Height; + // If there's some kind of import profile, we can transform to the + // output. + if (hasIccProfile) + { + // Fallback to sRGB. + importProfile = "srgb"; - // Use 64-bit unsigned type, to handle PNG decompression bombs. - if ((ulong)(inputWidth * inputHeight) > MaxImageSize) - { - throw new Exception( - "Image is too large for processing. Width x height should be less than 71 megapixels."); - } + // Convert to sRGB using embedded or import profile. + exportProfile = "srgb"; - pageHeight = image.PageHeight; - hasIccProfile = image.Contains(VipsMetaIccName); - } - catch (VipsException e) - { - throw new Exception("Image has a corrupt header.", e); - } - finally - { - // We're done with the image; dispose early - image?.Dispose(); - } + // Use "perceptual" intent to better match *magick. + intent = Enums.Intent.Perceptual; + } + // Scaling calculations + var thumbnailWidth = width; + var thumbnailHeight = height; - string importProfile = null; - string exportProfile = null; - Enums.Intent? intent = null; + if (width > 0 && height > 0) // Fixed width and height + { + var xFactor = (double)inputWidth / width; + var yFactor = (double)pageHeight / height; - // If there's some kind of import profile, we can transform to the - // output. - if (hasIccProfile) + if (xFactor > yFactor) // Or: if (xFactor < yFactor) { - // Fallback to sRGB. - importProfile = "srgb"; - - // Convert to sRGB using embedded or import profile. - exportProfile = "srgb"; - - // Use "perceptual" intent to better match *magick. - intent = Enums.Intent.Perceptual; + thumbnailHeight = (int)Math.Round(pageHeight / xFactor); } - - // Scaling calculations - var thumbnailWidth = width; - var thumbnailHeight = height; - - if (width > 0 && height > 0) // Fixed width and height + else { - var xFactor = (double)inputWidth / width; - var yFactor = (double)pageHeight / height; - - if (xFactor > yFactor) // Or: if (xFactor < yFactor) - { - thumbnailHeight = (int)Math.Round(pageHeight / xFactor); - } - else - { - thumbnailWidth = (int)Math.Round(inputWidth / yFactor); - } + thumbnailWidth = (int)Math.Round(inputWidth / yFactor); } - else if (width > 0) // Fixed width + } + else if (width > 0) // Fixed width + { + if (size == Enums.Size.Force) { - if (size == Enums.Size.Force) - { - thumbnailHeight = pageHeight; - height = pageHeight; - } - else - { - // Auto height - var yFactor = (double)inputWidth / width; - height = (int)Math.Round(pageHeight / yFactor); - - // Height is missing, replace with a huuuge value to prevent - // reduction or enlargement in that axis - thumbnailHeight = VipsMaxCoord; - } + thumbnailHeight = pageHeight; + height = pageHeight; } - else if (height > 0) // Fixed height + else { - if (size == Enums.Size.Force) - { - thumbnailWidth = inputWidth; - width = inputWidth; - } - else - { - // Auto width - var xFactor = (double)pageHeight / height; - width = (int)Math.Round(inputWidth / xFactor); - - // Width is missing, replace with a huuuge value to prevent - // reduction or enlargement in that axis - thumbnailWidth = VipsMaxCoord; - } + // Auto height + var yFactor = (double)inputWidth / width; + height = (int)Math.Round(pageHeight / yFactor); + + // Height is missing, replace with a huuuge value to prevent + // reduction or enlargement in that axis + thumbnailHeight = VipsMaxCoord; } - else // Identity transform + } + else if (height > 0) // Fixed height + { + if (size == Enums.Size.Force) { thumbnailWidth = inputWidth; width = inputWidth; + } + else + { + // Auto width + var xFactor = (double)pageHeight / height; + width = (int)Math.Round(inputWidth / xFactor); - thumbnailHeight = pageHeight; - height = pageHeight; + // Width is missing, replace with a huuuge value to prevent + // reduction or enlargement in that axis + thumbnailWidth = VipsMaxCoord; } + } + else // Identity transform + { + thumbnailWidth = inputWidth; + width = inputWidth; + + thumbnailHeight = pageHeight; + height = pageHeight; + } - // Note: don't use "image.ThumbnailImage". Otherwise, none of the very fast - // shrink-on-load tricks are possible. This can make thumbnailing of large - // images extremely slow. - using var thumb = Image.ThumbnailBuffer(buffer, thumbnailWidth, stringOptions, thumbnailHeight, size, - importProfile: importProfile, exportProfile: exportProfile, intent: intent); + // Note: don't use "image.ThumbnailImage". Otherwise, none of the very fast + // shrink-on-load tricks are possible. This can make thumbnailing of large + // images extremely slow. + using var thumb = Image.ThumbnailBuffer(buffer, thumbnailWidth, stringOptions, thumbnailHeight, size, + importProfile: importProfile, exportProfile: exportProfile, intent: intent); - thumb.WriteToFile("thumbnail.webp", new VOption - { - {"keep", Enums.ForeignKeep.None} - }); + thumb.WriteToFile("thumbnail.webp", new VOption + { + {"keep", Enums.ForeignKeep.None} + }); - // Or: - /*buffer = thumb.WriteToBuffer(".webp", new VOption - { - {"keep", Enums.ForeignKeep.None} - });*/ + // Or: + /*buffer = thumb.WriteToBuffer(".webp", new VOption + { + {"keep", Enums.ForeignKeep.None} + });*/ - Console.WriteLine("See thumbnail.webp"); - } + Console.WriteLine("See thumbnail.webp"); } -#pragma warning restore CS0162 // Unreachable code detected -} \ No newline at end of file +} +#pragma warning restore CS0162 // Unreachable code detected \ No newline at end of file diff --git a/samples/NetVips.Samples/Samples/ThumbnailStream.cs b/samples/NetVips.Samples/Samples/ThumbnailStream.cs index c7f0c260..24a363a2 100644 --- a/samples/NetVips.Samples/Samples/ThumbnailStream.cs +++ b/samples/NetVips.Samples/Samples/ThumbnailStream.cs @@ -1,25 +1,24 @@ -namespace NetVips.Samples -{ - using System; - using System.IO; +using System; +using System.IO; - public class ThumbnailStream : ISample - { - public string Name => "Thumbnail a file stream"; - public string Category => "Streaming"; +namespace NetVips.Samples; - public const string Filename = "images/lichtenstein.jpg"; +public class ThumbnailStream : ISample +{ + public string Name => "Thumbnail a file stream"; + public string Category => "Streaming"; - public void Execute(string[] args) - { - using var input = File.OpenRead(Filename); - using var thumbnail = Image.ThumbnailStream(input, 300, height: 300); - Console.WriteLine(thumbnail.ToString()); + public const string Filename = "images/lichtenstein.jpg"; + + public void Execute(string[] args) + { + using var input = File.OpenRead(Filename); + using var thumbnail = Image.ThumbnailStream(input, 300, height: 300); + Console.WriteLine(thumbnail.ToString()); - using var output = File.OpenWrite("thumbnail-stream.jpg"); - thumbnail.WriteToStream(output, ".jpg"); + using var output = File.OpenWrite("thumbnail-stream.jpg"); + thumbnail.WriteToStream(output, ".jpg"); - Console.WriteLine("See thumbnail-stream.jpg"); - } + Console.WriteLine("See thumbnail-stream.jpg"); } } \ No newline at end of file diff --git a/src/NetVips.Extensions/BitmapConverter.cs b/src/NetVips.Extensions/BitmapConverter.cs index 4e63b074..1678b1f2 100644 --- a/src/NetVips.Extensions/BitmapConverter.cs +++ b/src/NetVips.Extensions/BitmapConverter.cs @@ -1,429 +1,427 @@ -namespace NetVips.Extensions -{ - using System; - using System.Drawing; - using System.Drawing.Imaging; - using System.Runtime.InteropServices; - using Image = Image; +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; + +namespace NetVips.Extensions; +/// +/// Static class which provides conversion between and . +/// +public static class BitmapConverter +{ /// - /// Static class which provides conversion between and . + /// Guess the number of bands for a . /// - public static class BitmapConverter + /// + /// GDI+ does not support 16bpp images very well (even though they are still in the enumeration). + /// + /// to guess for. + /// The number of bands. + private static int GuessBands(PixelFormat pixelFormat) { - /// - /// Guess the number of bands for a . - /// - /// - /// GDI+ does not support 16bpp images very well (even though they are still in the enumeration). - /// - /// to guess for. - /// The number of bands. - private static int GuessBands(PixelFormat pixelFormat) + return pixelFormat switch { - return pixelFormat switch - { - PixelFormat.Format8bppIndexed => - /* Note: Maplut below will create a 3-band image */ - 1, - /*case PixelFormat.Format16bppGrayScale: - return 2;*/ - /*case PixelFormat.Format1bppIndexed:*/ - /*case PixelFormat.Format4bppIndexed:*/ - /*case PixelFormat.Format16bppRgb555:*/ - /*case PixelFormat.Format16bppRgb565:*/ - PixelFormat.Format24bppRgb => 3, - /*case PixelFormat.Format32bppRgb:*/ - PixelFormat.Format48bppRgb => 3, - /*case PixelFormat.Format16bppArgb1555:*/ - PixelFormat.Format32bppArgb => 4, - PixelFormat.Format32bppPArgb => 4, - PixelFormat.Format64bppArgb => 4, - PixelFormat.Format64bppPArgb => 4, - _ => throw new NotImplementedException($"GuessBands({pixelFormat}) is not yet implemented.") - }; - } + PixelFormat.Format8bppIndexed => + /* Note: Maplut below will create a 3-band image */ + 1, + /*case PixelFormat.Format16bppGrayScale: + return 2;*/ + /*case PixelFormat.Format1bppIndexed:*/ + /*case PixelFormat.Format4bppIndexed:*/ + /*case PixelFormat.Format16bppRgb555:*/ + /*case PixelFormat.Format16bppRgb565:*/ + PixelFormat.Format24bppRgb => 3, + /*case PixelFormat.Format32bppRgb:*/ + PixelFormat.Format48bppRgb => 3, + /*case PixelFormat.Format16bppArgb1555:*/ + PixelFormat.Format32bppArgb => 4, + PixelFormat.Format32bppPArgb => 4, + PixelFormat.Format64bppArgb => 4, + PixelFormat.Format64bppPArgb => 4, + _ => throw new NotImplementedException($"GuessBands({pixelFormat}) is not yet implemented.") + }; + } - /// - /// Guess the for a . - /// - /// - /// GDI+ does not support 16bpp images very well (even though they are still in the enumeration). - /// - /// to guess for. - /// The . - private static Enums.BandFormat GuessBandFormat(PixelFormat pixelFormat) + /// + /// Guess the for a . + /// + /// + /// GDI+ does not support 16bpp images very well (even though they are still in the enumeration). + /// + /// to guess for. + /// The . + private static Enums.BandFormat GuessBandFormat(PixelFormat pixelFormat) + { + return pixelFormat switch { - return pixelFormat switch - { - /*case PixelFormat.Format1bppIndexed:*/ - /*case PixelFormat.Format4bppIndexed:*/ - PixelFormat.Format8bppIndexed => Enums.BandFormat.Uchar, - /*case PixelFormat.Format16bppGrayScale:*/ - /*case PixelFormat.Format16bppRgb555:*/ - /*case PixelFormat.Format16bppRgb565:*/ - PixelFormat.Format24bppRgb => Enums.BandFormat.Uchar, - /*case PixelFormat.Format32bppRgb:*/ - /*case PixelFormat.Format16bppArgb1555:*/ - PixelFormat.Format32bppArgb => Enums.BandFormat.Uchar, - PixelFormat.Format32bppPArgb => Enums.BandFormat.Uchar, - PixelFormat.Format48bppRgb => Enums.BandFormat.Ushort, - PixelFormat.Format64bppArgb => Enums.BandFormat.Ushort, - PixelFormat.Format64bppPArgb => Enums.BandFormat.Ushort, - _ => throw new NotImplementedException($"GuessBandFormat({pixelFormat}) is not yet implemented.") - }; - } + /*case PixelFormat.Format1bppIndexed:*/ + /*case PixelFormat.Format4bppIndexed:*/ + PixelFormat.Format8bppIndexed => Enums.BandFormat.Uchar, + /*case PixelFormat.Format16bppGrayScale:*/ + /*case PixelFormat.Format16bppRgb555:*/ + /*case PixelFormat.Format16bppRgb565:*/ + PixelFormat.Format24bppRgb => Enums.BandFormat.Uchar, + /*case PixelFormat.Format32bppRgb:*/ + /*case PixelFormat.Format16bppArgb1555:*/ + PixelFormat.Format32bppArgb => Enums.BandFormat.Uchar, + PixelFormat.Format32bppPArgb => Enums.BandFormat.Uchar, + PixelFormat.Format48bppRgb => Enums.BandFormat.Ushort, + PixelFormat.Format64bppArgb => Enums.BandFormat.Ushort, + PixelFormat.Format64bppPArgb => Enums.BandFormat.Ushort, + _ => throw new NotImplementedException($"GuessBandFormat({pixelFormat}) is not yet implemented.") + }; + } - /// - /// Converts to . - /// - /// to be converted. - /// A new . - public static Image ToVips(this Bitmap src) + /// + /// Converts to . + /// + /// to be converted. + /// A new . + public static Image ToVips(this Bitmap src) + { + if (src == null) + throw new ArgumentNullException(nameof(src)); + + // Let LockBits convert the pixel data to Format24bppRgb for indexed + // (excluding Format8bppIndexed) and Format32bppRgb (the remaining + // 8 bits are not used anyway) images. This is faster than the pixel + // loops commented below and simplifies the code considerably. + var pf = + src.PixelFormat == PixelFormat.Format1bppIndexed || + src.PixelFormat == PixelFormat.Format4bppIndexed || + src.PixelFormat == PixelFormat.Format32bppRgb + ? PixelFormat.Format24bppRgb + : src.PixelFormat; + + var bands = GuessBands(pf); + var format = GuessBandFormat(pf); + var sizeofFormat = format == Enums.BandFormat.Uchar ? sizeof(byte) : sizeof(ushort); + + var w = src.Width; + var h = src.Height; + var stride = w * bands * sizeofFormat; + var size = stride * h; + + var rect = new Rectangle(0, 0, w, h); + BitmapData bd = null; + Image dst; + try { - if (src == null) - throw new ArgumentNullException(nameof(src)); - - // Let LockBits convert the pixel data to Format24bppRgb for indexed - // (excluding Format8bppIndexed) and Format32bppRgb (the remaining - // 8 bits are not used anyway) images. This is faster than the pixel - // loops commented below and simplifies the code considerably. - var pf = - src.PixelFormat == PixelFormat.Format1bppIndexed || - src.PixelFormat == PixelFormat.Format4bppIndexed || - src.PixelFormat == PixelFormat.Format32bppRgb - ? PixelFormat.Format24bppRgb - : src.PixelFormat; - - var bands = GuessBands(pf); - var format = GuessBandFormat(pf); - var sizeofFormat = format == Enums.BandFormat.Uchar ? sizeof(byte) : sizeof(ushort); - - var w = src.Width; - var h = src.Height; - var stride = w * bands * sizeofFormat; - var size = stride * h; - - var rect = new Rectangle(0, 0, w, h); - BitmapData bd = null; - Image dst; - try - { - bd = src.LockBits(rect, ImageLockMode.ReadOnly, pf); + bd = src.LockBits(rect, ImageLockMode.ReadOnly, pf); - switch (pf) + switch (pf) + { + /*case PixelFormat.Format1bppIndexed: { - /*case PixelFormat.Format1bppIndexed: - { - var buffer = new byte[size]; + var buffer = new byte[size]; - for (var y = 0; y < h; y++) - { - for (int bytePos = 0, x = 0; bytePos < bd.Stride; bytePos++) - { - if (x >= w) - continue; - - var b = Marshal.ReadByte(bd.Scan0, y * bd.Stride + bytePos); - for (var i = 0; i < 8; i++, x++) - { - if (x >= w) - break; - - var colorIndex = (b & 0x80) == 0x80 ? 1 : 0; - buffer[y * stride + x * 3 + 0] = src.Palette.Entries[colorIndex].R; - buffer[y * stride + x * 3 + 1] = src.Palette.Entries[colorIndex].G; - buffer[y * stride + x * 3 + 2] = src.Palette.Entries[colorIndex].B; - b <<= 1; - } - } - } - - return Image.NewFromMemory(buffer, w, h, bands, format); - } - case PixelFormat.Format4bppIndexed: + for (var y = 0; y < h; y++) { - var buffer = new byte[size]; - - for (var y = 0; y < h; y++) + for (int bytePos = 0, x = 0; bytePos < bd.Stride; bytePos++) { - for (var x = 0; x < w; x++) + if (x >= w) + continue; + + var b = Marshal.ReadByte(bd.Scan0, y * bd.Stride + bytePos); + for (var i = 0; i < 8; i++, x++) { - var b = Marshal.ReadByte(bd.Scan0, y * bd.Stride + (x >> 1)); + if (x >= w) + break; - var colorIndex = (x & 1) == 0 ? b >> 4 : b & 0x0F; + var colorIndex = (b & 0x80) == 0x80 ? 1 : 0; buffer[y * stride + x * 3 + 0] = src.Palette.Entries[colorIndex].R; buffer[y * stride + x * 3 + 1] = src.Palette.Entries[colorIndex].G; buffer[y * stride + x * 3 + 2] = src.Palette.Entries[colorIndex].B; + b <<= 1; } } - - return Image.NewFromMemory(buffer, w, h, bands, format); } - case PixelFormat.Format32bppRgb: - { - var buffer = new byte[size]; - for (var y = 0; y < h; y++) + return Image.NewFromMemory(buffer, w, h, bands, format); + } + case PixelFormat.Format4bppIndexed: + { + var buffer = new byte[size]; + + for (var y = 0; y < h; y++) + { + for (var x = 0; x < w; x++) { - for (var x = 0; x < w; x++) - { - // The remaining 8 bits are not used - Marshal.Copy(bd.Scan0 + y * bd.Stride + x * 4, buffer, y * stride + x * 3, 3); - } + var b = Marshal.ReadByte(bd.Scan0, y * bd.Stride + (x >> 1)); + + var colorIndex = (x & 1) == 0 ? b >> 4 : b & 0x0F; + buffer[y * stride + x * 3 + 0] = src.Palette.Entries[colorIndex].R; + buffer[y * stride + x * 3 + 1] = src.Palette.Entries[colorIndex].G; + buffer[y * stride + x * 3 + 2] = src.Palette.Entries[colorIndex].B; } + } - dst = Image.NewFromMemory(buffer, w, h, bands, format); - break; - }*/ - case PixelFormat.Format24bppRgb when bd.Stride == stride: - case PixelFormat.Format8bppIndexed when bd.Stride == stride: - case PixelFormat.Format32bppArgb: - case PixelFormat.Format32bppPArgb: - case PixelFormat.Format48bppRgb when bd.Stride == stride: - case PixelFormat.Format64bppArgb: - case PixelFormat.Format64bppPArgb: - // bd.Stride is aligned to a multiple of 4 - dst = Image.NewFromMemoryCopy(bd.Scan0, (ulong)size, w, h, bands, format); - break; - default: - { - var buffer = new byte[size]; + return Image.NewFromMemory(buffer, w, h, bands, format); + } + case PixelFormat.Format32bppRgb: + { + var buffer = new byte[size]; - // Copy the bytes from src to the managed array for each scanline - for (var y = 0; y < h; y++) + for (var y = 0; y < h; y++) + { + for (var x = 0; x < w; x++) { - Marshal.Copy(bd.Scan0 + y * bd.Stride, buffer, y * stride, stride); + // The remaining 8 bits are not used + Marshal.Copy(bd.Scan0 + y * bd.Stride + x * 4, buffer, y * stride + x * 3, 3); } + } - dst = Image.NewFromMemory(buffer, w, h, bands, format); - break; + dst = Image.NewFromMemory(buffer, w, h, bands, format); + break; + }*/ + case PixelFormat.Format24bppRgb when bd.Stride == stride: + case PixelFormat.Format8bppIndexed when bd.Stride == stride: + case PixelFormat.Format32bppArgb: + case PixelFormat.Format32bppPArgb: + case PixelFormat.Format48bppRgb when bd.Stride == stride: + case PixelFormat.Format64bppArgb: + case PixelFormat.Format64bppPArgb: + // bd.Stride is aligned to a multiple of 4 + dst = Image.NewFromMemoryCopy(bd.Scan0, (ulong)size, w, h, bands, format); + break; + default: + { + var buffer = new byte[size]; + + // Copy the bytes from src to the managed array for each scanline + for (var y = 0; y < h; y++) + { + Marshal.Copy(bd.Scan0 + y * bd.Stride, buffer, y * stride, stride); } + + dst = Image.NewFromMemory(buffer, w, h, bands, format); + break; } } - finally + } + finally + { + if (bd != null) + src.UnlockBits(bd); + } + + if (pf == PixelFormat.Format8bppIndexed) + { + var palette = new byte[src.Palette.Entries.Length * 3]; + for (var i = 0; i < src.Palette.Entries.Length; i++) { - if (bd != null) - src.UnlockBits(bd); + palette[i * 3 + 0] = src.Palette.Entries[i].R; + palette[i * 3 + 1] = src.Palette.Entries[i].G; + palette[i * 3 + 2] = src.Palette.Entries[i].B; } - if (pf == PixelFormat.Format8bppIndexed) + using var lut = Image.NewFromMemory(palette, src.Palette.Entries.Length, 1, 3, Enums.BandFormat.Uchar); + using (dst) { - var palette = new byte[src.Palette.Entries.Length * 3]; - for (var i = 0; i < src.Palette.Entries.Length; i++) + return dst.Maplut(lut); + } + } + + switch (bands) + { + case 3: + // Switch from BGR to RGB + using (dst) { - palette[i * 3 + 0] = src.Palette.Entries[i].R; - palette[i * 3 + 1] = src.Palette.Entries[i].G; - palette[i * 3 + 2] = src.Palette.Entries[i].B; + var bgr = dst.Bandsplit(); + using var b = bgr[0]; + using var g = bgr[1]; + using var r = bgr[2]; + return r.Bandjoin(g, b); } - - using var lut = Image.NewFromMemory(palette, src.Palette.Entries.Length, 1, 3, Enums.BandFormat.Uchar); + case 4: + // Switch from BGRA to RGBA using (dst) { - return dst.Maplut(lut); + var bgra = dst.Bandsplit(); + using var b = bgra[0]; + using var g = bgra[1]; + using var r = bgra[2]; + using var a = bgra[3]; + + return r.Bandjoin(g, b, a); } - } + default: + return dst; + } + } - switch (bands) - { - case 3: - // Switch from BGR to RGB - using (dst) - { - var bgr = dst.Bandsplit(); - using var b = bgr[0]; - using var g = bgr[1]; - using var r = bgr[2]; - return r.Bandjoin(g, b); - } - case 4: - // Switch from BGRA to RGBA - using (dst) - { - var bgra = dst.Bandsplit(); - using var b = bgra[0]; - using var g = bgra[1]; - using var r = bgra[2]; - using var a = bgra[3]; + /// + /// Converts to . + /// + /// to be converted. + /// A new . + public static Image ToVips(this System.Drawing.Image src) + { + return ToVips((Bitmap)src); + } - return r.Bandjoin(g, b, a); - } - default: - return dst; - } - } + /// + /// Converts to . + /// + /// to be converted. + /// A new . + public static Bitmap ToBitmap(this Image src) + { + if (src == null) + throw new ArgumentNullException(nameof(src)); - /// - /// Converts to . - /// - /// to be converted. - /// A new . - public static Image ToVips(this System.Drawing.Image src) + // Ensure image is converted to sRGB + if (src.Bands >= 3) { - return ToVips((Bitmap)src); + src = src.Colourspace(Enums.Interpretation.Srgb); } - /// - /// Converts to . - /// - /// to be converted. - /// A new . - public static Bitmap ToBitmap(this Image src) + PixelFormat pf; + switch (src.Bands) { - if (src == null) - throw new ArgumentNullException(nameof(src)); - - // Ensure image is converted to sRGB - if (src.Bands >= 3) - { + case 1: + // when src.Interpretation == Enums.Interpretation.Multiband || + // src.Interpretation == Enums.Interpretation.Bw || + // src.Interpretation == Enums.Interpretation.Matrix + pf = PixelFormat.Format8bppIndexed; + + // Ensure image is casted to uint8 (unsigned char) + src = src.Cast(Enums.BandFormat.Uchar); + + break; + case 2 when src.Interpretation == Enums.Interpretation.Grey16: + // Convert to sRGB, since Format16bppGrayScale appears to be unsupported by GDI+. + // See: https://stackoverflow.com/a/19706842/10952119 src = src.Colourspace(Enums.Interpretation.Srgb); - } - PixelFormat pf; - switch (src.Bands) - { - case 1: - // when src.Interpretation == Enums.Interpretation.Multiband || - // src.Interpretation == Enums.Interpretation.Bw || - // src.Interpretation == Enums.Interpretation.Matrix - pf = PixelFormat.Format8bppIndexed; + goto case 4; + case 2: + // when src.Interpretation == Enums.Interpretation.Multiband || + // src.Interpretation == Enums.Interpretation.Bw + // Add an additional band + src = src.Bandjoin(255); - // Ensure image is casted to uint8 (unsigned char) - src = src.Cast(Enums.BandFormat.Uchar); + goto case 3; + case 3: + pf = src.Format == Enums.BandFormat.Ushort + ? PixelFormat.Format48bppRgb + : PixelFormat.Format24bppRgb; - break; - case 2 when src.Interpretation == Enums.Interpretation.Grey16: - // Convert to sRGB, since Format16bppGrayScale appears to be unsupported by GDI+. - // See: https://stackoverflow.com/a/19706842/10952119 - src = src.Colourspace(Enums.Interpretation.Srgb); - - goto case 4; - case 2: - // when src.Interpretation == Enums.Interpretation.Multiband || - // src.Interpretation == Enums.Interpretation.Bw - // Add an additional band - src = src.Bandjoin(255); - - goto case 3; - case 3: - pf = src.Format == Enums.BandFormat.Ushort - ? PixelFormat.Format48bppRgb - : PixelFormat.Format24bppRgb; - - { - // Switch from RGB to BGR - var rgb = src.Bandsplit(); - using var r = rgb[0]; - using var g = rgb[1]; - using var b = rgb[2]; - - using (src) - { - src = b.Bandjoin(g, r); - } - } - - break; - case 4: - pf = src.Format == Enums.BandFormat.Ushort - ? PixelFormat.Format64bppArgb - : PixelFormat.Format32bppArgb; + { + // Switch from RGB to BGR + var rgb = src.Bandsplit(); + using var r = rgb[0]; + using var g = rgb[1]; + using var b = rgb[2]; + using (src) { - // Switch from RGBA to BGRA - var rgba = src.Bandsplit(); - using var r = rgba[0]; - using var g = rgba[1]; - using var b = rgba[2]; - using var a = rgba[3]; - - using (src) - { - src = b.Bandjoin(g, r, a); - } + src = b.Bandjoin(g, r); } - - break; - default: - throw new NotImplementedException( - $"Number of bands must be 1 or in the in the range of 3 to 4. Got: {src.Bands}"); } - if (src.Format != Enums.BandFormat.Uchar || src.Format != Enums.BandFormat.Ushort) + break; + case 4: + pf = src.Format == Enums.BandFormat.Ushort + ? PixelFormat.Format64bppArgb + : PixelFormat.Format32bppArgb; + { - // Pixel formats other than uchar and ushort needs to be casted to uint8 (unsigned char) + // Switch from RGBA to BGRA + var rgba = src.Bandsplit(); + using var r = rgba[0]; + using var g = rgba[1]; + using var b = rgba[2]; + using var a = rgba[3]; + using (src) { - src = src.Cast(Enums.BandFormat.Uchar); + src = b.Bandjoin(g, r, a); } } - var dst = new Bitmap(src.Width, src.Height, pf); + break; + default: + throw new NotImplementedException( + $"Number of bands must be 1 or in the in the range of 3 to 4. Got: {src.Bands}"); + } - // We need to generate a greyscale palette for 8bpp images - if (pf == PixelFormat.Format8bppIndexed) + if (src.Format != Enums.BandFormat.Uchar || src.Format != Enums.BandFormat.Ushort) + { + // Pixel formats other than uchar and ushort needs to be casted to uint8 (unsigned char) + using (src) { - var plt = dst.Palette; - for (var x = 0; x < 256; x++) - { - plt.Entries[x] = Color.FromArgb(x, x, x); - } + src = src.Cast(Enums.BandFormat.Uchar); + } + } - dst.Palette = plt; + var dst = new Bitmap(src.Width, src.Height, pf); + + // We need to generate a greyscale palette for 8bpp images + if (pf == PixelFormat.Format8bppIndexed) + { + var plt = dst.Palette; + for (var x = 0; x < 256; x++) + { + plt.Entries[x] = Color.FromArgb(x, x, x); } - var w = src.Width; - var h = src.Height; - var bands = src.Bands; - var rect = new Rectangle(0, 0, w, h); - BitmapData bd = null; - var memory = IntPtr.Zero; + dst.Palette = plt; + } + + var w = src.Width; + var h = src.Height; + var bands = src.Bands; + var rect = new Rectangle(0, 0, w, h); + BitmapData bd = null; + var memory = IntPtr.Zero; + + try + { + bd = dst.LockBits(rect, ImageLockMode.WriteOnly, pf); + var dstSize = (ulong)(bd.Stride * h); + ulong srcSize; - try + using (src) { - bd = dst.LockBits(rect, ImageLockMode.WriteOnly, pf); - var dstSize = (ulong)(bd.Stride * h); - ulong srcSize; + memory = src.WriteToMemory(out srcSize); + } - using (src) + // bd.Stride is aligned to a multiple of 4 + if (dstSize == srcSize) + { + unsafe { - memory = src.WriteToMemory(out srcSize); + Buffer.MemoryCopy(memory.ToPointer(), bd.Scan0.ToPointer(), srcSize, srcSize); } + } + else + { + var offset = w * bands; - // bd.Stride is aligned to a multiple of 4 - if (dstSize == srcSize) + // Copy the bytes from src to dst for each scanline + for (var y = 0; y < h; y++) { + var pSrc = memory + y * offset; + var pDst = bd.Scan0 + y * bd.Stride; + unsafe { - Buffer.MemoryCopy(memory.ToPointer(), bd.Scan0.ToPointer(), srcSize, srcSize); + Buffer.MemoryCopy(pSrc.ToPointer(), pDst.ToPointer(), offset, offset); } } - else - { - var offset = w * bands; - - // Copy the bytes from src to dst for each scanline - for (var y = 0; y < h; y++) - { - var pSrc = memory + y * offset; - var pDst = bd.Scan0 + y * bd.Stride; - - unsafe - { - Buffer.MemoryCopy(pSrc.ToPointer(), pDst.ToPointer(), offset, offset); - } - } - } - } - finally - { - if (bd != null) - dst.UnlockBits(bd); - if (memory != IntPtr.Zero) - NetVips.Free(memory); } - - return dst; } + finally + { + if (bd != null) + dst.UnlockBits(bd); + if (memory != IntPtr.Zero) + NetVips.Free(memory); + } + + return dst; } } \ No newline at end of file diff --git a/src/NetVips/Cache.cs b/src/NetVips/Cache.cs index 9e19d235..66e3d1b6 100644 --- a/src/NetVips/Cache.cs +++ b/src/NetVips/Cache.cs @@ -1,50 +1,49 @@ -namespace NetVips -{ - using Internal; +using NetVips.Internal; + +namespace NetVips; +/// +/// A class around libvips' operation cache. +/// +public static class Cache +{ /// - /// A class around libvips' operation cache. + /// Gets or sets the maximum number of operations libvips keeps in cache. /// - public static class Cache + public static int Max { - /// - /// Gets or sets the maximum number of operations libvips keeps in cache. - /// - public static int Max - { - get => Vips.CacheGetMax(); - set => Vips.CacheSetMax(value); - } + get => Vips.CacheGetMax(); + set => Vips.CacheSetMax(value); + } - /// - /// Gets or sets the maximum amount of tracked memory allowed. - /// - public static ulong MaxMem - { - get => Vips.CacheGetMaxMem(); - set => Vips.CacheSetMaxMem(value); - } + /// + /// Gets or sets the maximum amount of tracked memory allowed. + /// + public static ulong MaxMem + { + get => Vips.CacheGetMaxMem(); + set => Vips.CacheSetMaxMem(value); + } - /// - /// Gets or sets the maximum amount of tracked files allowed. - /// - public static int MaxFiles - { - get => Vips.CacheGetMaxFiles(); - set => Vips.CacheSetMaxFiles(value); - } + /// + /// Gets or sets the maximum amount of tracked files allowed. + /// + public static int MaxFiles + { + get => Vips.CacheGetMaxFiles(); + set => Vips.CacheSetMaxFiles(value); + } - /// - /// Gets the current number of operations in cache. - /// - public static int Size => Vips.CacheGetSize(); + /// + /// Gets the current number of operations in cache. + /// + public static int Size => Vips.CacheGetSize(); - /// - /// Enable or disable libvips cache tracing. - /// - public static bool Trace - { - set => Vips.CacheSetTrace(value); - } + /// + /// Enable or disable libvips cache tracing. + /// + public static bool Trace + { + set => Vips.CacheSetTrace(value); } } \ No newline at end of file diff --git a/src/NetVips/Connection.cs b/src/NetVips/Connection.cs index 799df42d..7a0b45b7 100644 --- a/src/NetVips/Connection.cs +++ b/src/NetVips/Connection.cs @@ -1,35 +1,34 @@ -namespace NetVips +using System; + +namespace NetVips; + +/// +/// The abstract base Connection class. +/// +public abstract class Connection : VipsObject { - using System; + /// + internal Connection(IntPtr pointer) : base(pointer) + { + } /// - /// The abstract base Connection class. + /// Get the filename associated with a connection. Return if there + /// is no associated file. /// - public abstract class Connection : VipsObject + /// The filename associated with this connection or . + public string GetFileName() { - /// - internal Connection(IntPtr pointer) : base(pointer) - { - } - - /// - /// Get the filename associated with a connection. Return if there - /// is no associated file. - /// - /// The filename associated with this connection or . - public string GetFileName() - { - return Internal.VipsConnection.FileName(this).ToUtf8String(); - } + return Internal.VipsConnection.FileName(this).ToUtf8String(); + } - /// - /// Make a human-readable name for a connection suitable for error - /// messages. - /// - /// The human-readable name for this connection. - public string GetNick() - { - return Internal.VipsConnection.Nick(this).ToUtf8String(); - } + /// + /// Make a human-readable name for a connection suitable for error + /// messages. + /// + /// The human-readable name for this connection. + public string GetNick() + { + return Internal.VipsConnection.Nick(this).ToUtf8String(); } } \ No newline at end of file diff --git a/src/NetVips/Enums.cs b/src/NetVips/Enums.cs index 04d0a4ad..48a9e687 100644 --- a/src/NetVips/Enums.cs +++ b/src/NetVips/Enums.cs @@ -1,1304 +1,1303 @@ -namespace NetVips +using System; + +namespace NetVips; + +/// +/// This module contains the various libvips enums. +/// +public static class Enums { - using System; + #region semi-generated enums /// - /// This module contains the various libvips enums. + /// The type of access an operation has to supply. /// - public static class Enums + /// + /// See for example . + /// + public enum Access { - #region semi-generated enums + /// Requests can come in any order. + Random = 0, // "random" /// - /// The type of access an operation has to supply. + /// Means requests will be top-to-bottom, but with some + /// amount of buffering behind the read point for small non-local + /// accesses. /// - /// - /// See for example . - /// - public enum Access - { - /// Requests can come in any order. - Random = 0, // "random" - - /// - /// Means requests will be top-to-bottom, but with some - /// amount of buffering behind the read point for small non-local - /// accesses. - /// - Sequential = 1, // "sequential" - - /// Top-to-bottom without a buffer. - SequentialUnbuffered = 2 // "sequential-unbuffered" - } + Sequential = 1, // "sequential" - /// - /// Various types of alignment. - /// - /// - /// See for example . - /// - public enum Align - { - /// Align on the low coordinate edge. - Low = 0, // "low" + /// Top-to-bottom without a buffer. + SequentialUnbuffered = 2 // "sequential-unbuffered" + } - /// Align on the centre. - Centre = 1, // "centre" + /// + /// Various types of alignment. + /// + /// + /// See for example . + /// + public enum Align + { + /// Align on the low coordinate edge. + Low = 0, // "low" - /// Align on the high coordinate edge. - High = 2 // "high" - } + /// Align on the centre. + Centre = 1, // "centre" - /// - /// Various fixed 90 degree rotation angles. - /// - /// - /// See for example . - /// - public enum Angle - { - /// No rotate. - D0 = 0, // "d0" + /// Align on the high coordinate edge. + High = 2 // "high" + } - /// 90 degrees clockwise. - D90 = 1, // "d90" + /// + /// Various fixed 90 degree rotation angles. + /// + /// + /// See for example . + /// + public enum Angle + { + /// No rotate. + D0 = 0, // "d0" - /// 180 degrees. - D180 = 2, // "d180" + /// 90 degrees clockwise. + D90 = 1, // "d90" - /// 90 degrees anti-clockwise. - D270 = 3 // "d270" - } + /// 180 degrees. + D180 = 2, // "d180" - /// - /// Various fixed 45 degree rotation angles. - /// - /// - /// See for example . - /// - public enum Angle45 - { - /// No rotate. - D0 = 0, // "d0" + /// 90 degrees anti-clockwise. + D270 = 3 // "d270" + } - /// 45 degrees clockwise. - D45 = 1, // "d45" + /// + /// Various fixed 45 degree rotation angles. + /// + /// + /// See for example . + /// + public enum Angle45 + { + /// No rotate. + D0 = 0, // "d0" - /// 90 degrees clockwise. - D90 = 2, // "d90" + /// 45 degrees clockwise. + D45 = 1, // "d45" - /// 135 degrees clockwise. - D135 = 3, // "d135" + /// 90 degrees clockwise. + D90 = 2, // "d90" - /// 180 degrees. - D180 = 4, // "d180" + /// 135 degrees clockwise. + D135 = 3, // "d135" - /// 135 degrees anti-clockwise. - D225 = 5, // "d225" + /// 180 degrees. + D180 = 4, // "d180" - /// 90 degrees anti-clockwise. - D270 = 6, // "d270" + /// 135 degrees anti-clockwise. + D225 = 5, // "d225" - /// 45 degrees anti-clockwise. - D315 = 7 // "d315" - } + /// 90 degrees anti-clockwise. + D270 = 6, // "d270" - /// - /// The format of image bands. - /// - /// - /// The format used for each band element. Each corresponds to a native C type - /// for the current machine. - /// - public enum BandFormat - { - /// Invalid setting. - Notset = -1, // "notset" + /// 45 degrees anti-clockwise. + D315 = 7 // "d315" + } - /// unsigned char format. - Uchar = 0, // "uchar" + /// + /// The format of image bands. + /// + /// + /// The format used for each band element. Each corresponds to a native C type + /// for the current machine. + /// + public enum BandFormat + { + /// Invalid setting. + Notset = -1, // "notset" - /// char format. - Char = 1, // "char" + /// unsigned char format. + Uchar = 0, // "uchar" - /// unsigned short format. - Ushort = 2, // "ushort" + /// char format. + Char = 1, // "char" - /// short format. - Short = 3, // "short" + /// unsigned short format. + Ushort = 2, // "ushort" - /// unsigned int format. - Uint = 4, // "uint" + /// short format. + Short = 3, // "short" - /// int format. - Int = 5, // "int" + /// unsigned int format. + Uint = 4, // "uint" - /// float format. - Float = 6, // "float" + /// int format. + Int = 5, // "int" - /// complex (two floats) format. - Complex = 7, // "complex" + /// float format. + Float = 6, // "float" - /// double float format. - Double = 8, // "double" + /// complex (two floats) format. + Complex = 7, // "complex" - /// double complex (two double) format. - Dpcomplex = 9 // "dpcomplex" - } + /// double float format. + Double = 8, // "double" - /// - /// The various Porter-Duff and PDF blend modes. See . - /// - /// - /// The Cairo docs have a nice explanation of all the blend modes: - /// https://www.cairographics.org/operators - /// - /// The non-separable modes are not implemented. - /// - public enum BlendMode - { - /// Where the second object is drawn, the first is removed. - Clear = 0, // "clear" + /// double complex (two double) format. + Dpcomplex = 9 // "dpcomplex" + } - /// The second object is drawn as if nothing were below. - Source = 1, // "source" + /// + /// The various Porter-Duff and PDF blend modes. See . + /// + /// + /// The Cairo docs have a nice explanation of all the blend modes: + /// https://www.cairographics.org/operators + /// + /// The non-separable modes are not implemented. + /// + public enum BlendMode + { + /// Where the second object is drawn, the first is removed. + Clear = 0, // "clear" - /// The image shows what you would expect if you held two semi-transparent slides on top of each other. - Over = 2, // "over" + /// The second object is drawn as if nothing were below. + Source = 1, // "source" - /// The first object is removed completely, the second is only drawn where the first was. - In = 3, // "in" + /// The image shows what you would expect if you held two semi-transparent slides on top of each other. + Over = 2, // "over" - /// The second is drawn only where the first isn't. - Out = 4, // "out" + /// The first object is removed completely, the second is only drawn where the first was. + In = 3, // "in" - /// This leaves the first object mostly intact, but mixes both objects in the overlapping area. - Atop = 5, // "atop" + /// The second is drawn only where the first isn't. + Out = 4, // "out" - /// Leaves the first object untouched, the second is discarded completely. - Dest = 6, // "dest" + /// This leaves the first object mostly intact, but mixes both objects in the overlapping area. + Atop = 5, // "atop" - /// Like Over, but swaps the arguments. - DestOver = 7, // "dest-over" + /// Leaves the first object untouched, the second is discarded completely. + Dest = 6, // "dest" - /// Like In, but swaps the arguments. - DestIn = 8, // "dest-in" + /// Like Over, but swaps the arguments. + DestOver = 7, // "dest-over" - /// Like Out, but swaps the arguments. - DestOut = 9, // "dest-out" + /// Like In, but swaps the arguments. + DestIn = 8, // "dest-in" - /// Like Atop, but swaps the arguments. - DestAtop = 10, // "dest-atop" + /// Like Out, but swaps the arguments. + DestOut = 9, // "dest-out" - /// Something like a difference operator. - Xor = 11, // "xor" + /// Like Atop, but swaps the arguments. + DestAtop = 10, // "dest-atop" - /// A bit like adding the two images. - Add = 12, // "add" + /// Something like a difference operator. + Xor = 11, // "xor" - /// A bit like the darker of the two. - Saturate = 13, // "saturate" + /// A bit like adding the two images. + Add = 12, // "add" - /// At least as dark as the darker of the two inputs. - Multiply = 14, // "multiply" + /// A bit like the darker of the two. + Saturate = 13, // "saturate" - /// At least as light as the lighter of the inputs. - Screen = 15, // "screen" + /// At least as dark as the darker of the two inputs. + Multiply = 14, // "multiply" - /// Multiplies or screens colors, depending on the lightness. - Overlay = 16, // "overlay" + /// At least as light as the lighter of the inputs. + Screen = 15, // "screen" - /// The darker of each component. - Darken = 17, // "darken" + /// Multiplies or screens colors, depending on the lightness. + Overlay = 16, // "overlay" - /// The lighter of each component. - Lighten = 18, // "lighten" + /// The darker of each component. + Darken = 17, // "darken" - /// Brighten first by a factor second. - ColourDodge = 19, // "colour-dodge" + /// The lighter of each component. + Lighten = 18, // "lighten" - /// Darken first by a factor of second. - ColourBurn = 20, // "colour-burn" + /// Brighten first by a factor second. + ColourDodge = 19, // "colour-dodge" - /// Multiply or screen, depending on lightness. - HardLight = 21, // "hard-light" + /// Darken first by a factor of second. + ColourBurn = 20, // "colour-burn" - /// Darken or lighten, depending on lightness. - SoftLight = 22, // "soft-light" + /// Multiply or screen, depending on lightness. + HardLight = 21, // "hard-light" - /// Difference of the two. - Difference = 23, // "difference" + /// Darken or lighten, depending on lightness. + SoftLight = 22, // "soft-light" - /// Somewhat like Difference, but lower-contrast. - Exclusion = 24 // "exclusion" - } + /// Difference of the two. + Difference = 23, // "difference" - /// - /// How pixels are coded. - /// - /// - /// Normally, pixels are uncoded and can be manipulated as you would expect. - /// However some file formats code pixels for compression, and sometimes it's - /// useful to be able to manipulate images in the coded format. - /// - public enum Coding - { - /// Invalid setting. - Error = -1, // "error" + /// Somewhat like Difference, but lower-contrast. + Exclusion = 24 // "exclusion" + } - /// Pixels are not coded. - None = 0, // "none" + /// + /// How pixels are coded. + /// + /// + /// Normally, pixels are uncoded and can be manipulated as you would expect. + /// However some file formats code pixels for compression, and sometimes it's + /// useful to be able to manipulate images in the coded format. + /// + public enum Coding + { + /// Invalid setting. + Error = -1, // "error" - /// Pixels encode 3 float CIELAB values as 4 uchar. - Labq = 2, // "labq" + /// Pixels are not coded. + None = 0, // "none" - /// Pixels encode 3 float RGB as 4 uchar (Radiance coding). - Rad = 6 // "rad" - } + /// Pixels encode 3 float CIELAB values as 4 uchar. + Labq = 2, // "labq" - /// - /// How to combine passes. - /// - /// - /// See for example . - /// - public enum Combine - { - /// Take the maximum of all values. - Max = 0, // "max" + /// Pixels encode 3 float RGB as 4 uchar (Radiance coding). + Rad = 6 // "rad" + } - /// Take the sum of all values. - Sum = 1, // "sum" + /// + /// How to combine passes. + /// + /// + /// See for example . + /// + public enum Combine + { + /// Take the maximum of all values. + Max = 0, // "max" - /// Take the minimum value. - Min = 2 // "min" - } + /// Take the sum of all values. + Sum = 1, // "sum" - /// - /// How to combine pixels. - /// - /// - /// Operations like need to be told how to - /// combine images from two sources. See also . - /// - public enum CombineMode - { - /// Set pixels to the new value. - Set = 0, // "set" + /// Take the minimum value. + Min = 2 // "min" + } - /// Add pixels. - Add = 1 // "add" - } + /// + /// How to combine pixels. + /// + /// + /// Operations like need to be told how to + /// combine images from two sources. See also . + /// + public enum CombineMode + { + /// Set pixels to the new value. + Set = 0, // "set" - /// - /// A direction on a compass. Used for , for example. - /// - public enum CompassDirection - { - /// Centre - Centre = 0, // "centre" + /// Add pixels. + Add = 1 // "add" + } - /// North - North = 1, // "north" + /// + /// A direction on a compass. Used for , for example. + /// + public enum CompassDirection + { + /// Centre + Centre = 0, // "centre" - /// East - East = 2, // "east" + /// North + North = 1, // "north" - /// South - South = 3, // "south" + /// East + East = 2, // "east" - /// West - West = 4, // "west" + /// South + South = 3, // "south" - /// North-east - NorthEast = 5, // "north-east" + /// West + West = 4, // "west" - /// South-east - SouthEast = 6, // "south-east" + /// North-east + NorthEast = 5, // "north-east" - /// South-west - SouthWest = 7, // "south-west" + /// South-east + SouthEast = 6, // "south-east" - /// North-west - NorthWest = 8 // "north-west" - } + /// South-west + SouthWest = 7, // "south-west" - /// - /// A hint about the kind of demand geometry VIPS images prefer. - /// - public enum DemandStyle - { - /// Invalid setting. - Error = -1, // "error" + /// North-west + NorthWest = 8 // "north-west" + } - /// Demand in small (typically 64x64 pixel) tiles. - Smalltile = 0, // "smalltile" + /// + /// A hint about the kind of demand geometry VIPS images prefer. + /// + public enum DemandStyle + { + /// Invalid setting. + Error = -1, // "error" - /// Demand in fat (typically 10 pixel high) strips. - Fatstrip = 1, // "fatstrip" + /// Demand in small (typically 64x64 pixel) tiles. + Smalltile = 0, // "smalltile" - /// Demand in thin (typically 1 pixel high) strips. - Thinstrip = 2 // "thinstrip" - } + /// Demand in fat (typically 10 pixel high) strips. + Fatstrip = 1, // "fatstrip" - /// - /// A direction. - /// - /// - /// Operations like need to be told whether to flip - /// left-right or top-bottom. - /// - public enum Direction - { - /// left-right. - Horizontal = 0, // "horizontal" + /// Demand in thin (typically 1 pixel high) strips. + Thinstrip = 2 // "thinstrip" + } - /// top-bottom. - Vertical = 1 // "vertical" - } + /// + /// A direction. + /// + /// + /// Operations like need to be told whether to flip + /// left-right or top-bottom. + /// + public enum Direction + { + /// left-right. + Horizontal = 0, // "horizontal" - /// - /// How to extend image edges. - /// - /// - /// When the edges of an image are extended, you can specify how you want - /// the extension done. See , , - /// and so on. - /// - public enum Extend - { - /// New pixels are black, ie. all bits are zero. - Black = 0, // "black" + /// top-bottom. + Vertical = 1 // "vertical" + } - /// Each new pixel takes the value of the nearest edge pixel. - Copy = 1, // "copy" + /// + /// How to extend image edges. + /// + /// + /// When the edges of an image are extended, you can specify how you want + /// the extension done. See , , + /// and so on. + /// + public enum Extend + { + /// New pixels are black, ie. all bits are zero. + Black = 0, // "black" - /// The image is tiled to fill the new area. - Repeat = 2, // "repeat" + /// Each new pixel takes the value of the nearest edge pixel. + Copy = 1, // "copy" - /// The image is reflected and tiled to reduce hash edges. - Mirror = 3, // "mirror" + /// The image is tiled to fill the new area. + Repeat = 2, // "repeat" - /// New pixels are white, ie. all bits are set. - White = 4, // "white" + /// The image is reflected and tiled to reduce hash edges. + Mirror = 3, // "mirror" - /// Colour set from the @background property. - Background = 5 // "background" - } + /// New pixels are white, ie. all bits are set. + White = 4, // "white" - /// - /// How sensitive loaders are to errors, from never stop (very insensitive), to - /// stop on the smallest warning (very sensitive). - /// - /// Each one implies the ones before it, so implies - /// . - /// - public enum FailOn - { - /// Never stop, - None = 0, // "none" + /// Colour set from the @background property. + Background = 5 // "background" + } - /// Stop on image truncated, nothing else. - Truncated = 1, // "truncated" + /// + /// How sensitive loaders are to errors, from never stop (very insensitive), to + /// stop on the smallest warning (very sensitive). + /// + /// Each one implies the ones before it, so implies + /// . + /// + public enum FailOn + { + /// Never stop, + None = 0, // "none" - /// Stop on serious error or truncation. - Error = 2, // "error" + /// Stop on image truncated, nothing else. + Truncated = 1, // "truncated" - /// Stop on anything, even warnings. - Warning = 3 // "warning" - } + /// Stop on serious error or truncation. + Error = 2, // "error" - /// - /// The container type of the pyramid. - /// - /// - /// See for example . - /// - public enum ForeignDzContainer - { - /// Write tiles to the filesystem. - Fs = 0, // "fs" + /// Stop on anything, even warnings. + Warning = 3 // "warning" + } - /// Write tiles to a zip file. - Zip = 1, // "zip" + /// + /// The container type of the pyramid. + /// + /// + /// See for example . + /// + public enum ForeignDzContainer + { + /// Write tiles to the filesystem. + Fs = 0, // "fs" - /// Write to a szi file. - Szi = 2 // "szi" - } + /// Write tiles to a zip file. + Zip = 1, // "zip" - /// - /// How many pyramid layers to create. - /// - /// - /// See for example . - /// - public enum ForeignDzDepth - { - /// Create layers down to 1x1 pixel. - Onepixel = 0, // "onepixel" + /// Write to a szi file. + Szi = 2 // "szi" + } - /// Create layers down to 1x1 tile. - Onetile = 1, // "onetile" + /// + /// How many pyramid layers to create. + /// + /// + /// See for example . + /// + public enum ForeignDzDepth + { + /// Create layers down to 1x1 pixel. + Onepixel = 0, // "onepixel" - /// Only create a single layer. - One = 2 // "one" - } + /// Create layers down to 1x1 tile. + Onetile = 1, // "onetile" - /// - /// What directory layout and metadata standard to use. - /// - public enum ForeignDzLayout - { - /// Use DeepZoom directory layout. - Dz = 0, // "dz" + /// Only create a single layer. + One = 2 // "one" + } - /// Use Zoomify directory layout. - Zoomify = 1, // "zoomify" + /// + /// What directory layout and metadata standard to use. + /// + public enum ForeignDzLayout + { + /// Use DeepZoom directory layout. + Dz = 0, // "dz" - /// Use Google maps directory layout. - Google = 2, // "google" + /// Use Zoomify directory layout. + Zoomify = 1, // "zoomify" - /// Use IIIF v2 directory layout. - Iiif = 3, // "iiif" + /// Use Google maps directory layout. + Google = 2, // "google" - /// Use IIIF v3 directory layout - Iiif3 = 4 // "iiif3" - } + /// Use IIIF v2 directory layout. + Iiif = 3, // "iiif" - /// - /// The compression format to use inside a HEIF container. - /// - public enum ForeignHeifCompression - { - /// x265 - Hevc = 1, // "hevc" + /// Use IIIF v3 directory layout + Iiif3 = 4 // "iiif3" + } - /// x264 - Avc = 2, // "avc" + /// + /// The compression format to use inside a HEIF container. + /// + public enum ForeignHeifCompression + { + /// x265 + Hevc = 1, // "hevc" - /// JPEG - Jpeg = 3, // "jpeg" + /// x264 + Avc = 2, // "avc" - /// AOM - Av1 = 4 // "av1" - } + /// JPEG + Jpeg = 3, // "jpeg" - /// - /// The PNG filter to use. - /// - [Flags] - public enum ForeignPngFilter - { - /// No filtering. - None = 0x08, // "none" + /// AOM + Av1 = 4 // "av1" + } - /// Difference to the left. - Sub = 0x10, // "sub" + /// + /// The PNG filter to use. + /// + [Flags] + public enum ForeignPngFilter + { + /// No filtering. + None = 0x08, // "none" - /// Difference up. - Up = 0x20, // "up" + /// Difference to the left. + Sub = 0x10, // "sub" - /// Average of left and up. - Avg = 0x40, // "avg" + /// Difference up. + Up = 0x20, // "up" - /// Pick best neighbor predictor automatically. - Paeth = 0x80, // "paeth" + /// Average of left and up. + Avg = 0x40, // "avg" - /// Adaptive. - All = None | Sub | Up | Avg | Paeth // "all" - } + /// Pick best neighbor predictor automatically. + Paeth = 0x80, // "paeth" - /// - /// Which metadata to retain. - /// - [Flags] - public enum ForeignKeep - { - /// Don't attach metadata. - None = 0, // "none" + /// Adaptive. + All = None | Sub | Up | Avg | Paeth // "all" + } - /// Keep Exif metadata. - Exif = 1 << 0, // "exif" + /// + /// Which metadata to retain. + /// + [Flags] + public enum ForeignKeep + { + /// Don't attach metadata. + None = 0, // "none" - /// Keep XMP metadata. - Xmp = 1 << 1, // "xmp" + /// Keep Exif metadata. + Exif = 1 << 0, // "exif" - /// Keep IPTC metadata. - Iptc = 1 << 2, // "iptc" + /// Keep XMP metadata. + Xmp = 1 << 1, // "xmp" - /// Keep ICC metadata. - Icc = 1 << 3, // "icc" + /// Keep IPTC metadata. + Iptc = 1 << 2, // "iptc" - /// Keep other metadata (e.g. PNG comments and some TIFF tags) - Other = 1 << 4, // "other" + /// Keep ICC metadata. + Icc = 1 << 3, // "icc" - /// Keep all metadata. - All = Exif | Xmp | Iptc | Icc | Other // "all" - } + /// Keep other metadata (e.g. PNG comments and some TIFF tags) + Other = 1 << 4, // "other" - /// - /// The selected encoder to use. - /// - /// - /// If libheif hasn't been compiled with the selected encoder, it will - /// fallback to the default encoder based on . - /// - public enum ForeignHeifEncoder - { - /// Pick encoder automatically. - Auto = 0, // "auto" + /// Keep all metadata. + All = Exif | Xmp | Iptc | Icc | Other // "all" + } - /// AOM - Aom = 1, // "aom" + /// + /// The selected encoder to use. + /// + /// + /// If libheif hasn't been compiled with the selected encoder, it will + /// fallback to the default encoder based on . + /// + public enum ForeignHeifEncoder + { + /// Pick encoder automatically. + Auto = 0, // "auto" - /// RAV1E - Rav1e = 2, // "rav1e" + /// AOM + Aom = 1, // "aom" - /// SVT-AV1 - Svt = 3, // "svt" + /// RAV1E + Rav1e = 2, // "rav1e" - /// x265 - X265 = 4 // "x265" - } + /// SVT-AV1 + Svt = 3, // "svt" - /// - /// The netpbm file format to save as. - /// - public enum ForeignPpmFormat - { - /// Images are single bit. - Pbm = 0, // "pbm" + /// x265 + X265 = 4 // "x265" + } - /// Images are 8, 16, or 32-bits, one band. - Pgm = 1, // "pgm" + /// + /// The netpbm file format to save as. + /// + public enum ForeignPpmFormat + { + /// Images are single bit. + Pbm = 0, // "pbm" - /// Images are 8, 16, or 32-bits, three bands. - Ppm = 2, // "ppm" + /// Images are 8, 16, or 32-bits, one band. + Pgm = 1, // "pgm" - /// Images are 32-bit float pixels. - Pfm = 3, // "pfm" + /// Images are 8, 16, or 32-bits, three bands. + Ppm = 2, // "ppm" - /// Images are anymap images -- the image format is used to pick the saver. - Pnm = 4 // "pnm" - } + /// Images are 32-bit float pixels. + Pfm = 3, // "pfm" - /// - /// Set JPEG/HEIF subsampling mode. - /// - public enum ForeignSubsample - { - /// Prevent subsampling when quality > 90. - Auto = 0, // "auto" + /// Images are anymap images -- the image format is used to pick the saver. + Pnm = 4 // "pnm" + } - /// Always perform subsampling. - On = 1, // "on" + /// + /// Set JPEG/HEIF subsampling mode. + /// + public enum ForeignSubsample + { + /// Prevent subsampling when quality > 90. + Auto = 0, // "auto" - /// Never perform subsampling. - Off = 2 // "off" - } + /// Always perform subsampling. + On = 1, // "on" - /// - /// The compression types supported by the tiff writer. - /// - public enum ForeignTiffCompression - { - /// No compression. - None = 0, // "none" + /// Never perform subsampling. + Off = 2 // "off" + } - /// JPEG compression. - Jpeg = 1, // "jpeg" + /// + /// The compression types supported by the tiff writer. + /// + public enum ForeignTiffCompression + { + /// No compression. + None = 0, // "none" - /// Deflate (zip) compression. - Deflate = 2, // "deflate" + /// JPEG compression. + Jpeg = 1, // "jpeg" - /// Packbits compression. - Packbits = 3, // "packbits" + /// Deflate (zip) compression. + Deflate = 2, // "deflate" - /// Fax4 compression. - Ccittfax4 = 4, // "ccittfax4" + /// Packbits compression. + Packbits = 3, // "packbits" - /// LZW compression. - Lzw = 5, // "lzw" + /// Fax4 compression. + Ccittfax4 = 4, // "ccittfax4" - /// WebP compression. - Webp = 6, // "webp" + /// LZW compression. + Lzw = 5, // "lzw" - /// ZSTD compression. - Zstd = 7, // "zstd" + /// WebP compression. + Webp = 6, // "webp" - /// JP2K compression. - Jp2k = 8 // "jp2k" - } + /// ZSTD compression. + Zstd = 7, // "zstd" - /// - /// The predictor can help deflate and lzw compression. - /// The values are fixed by the tiff library. - /// - public enum ForeignTiffPredictor - { - /// No prediction. - None = 1, // "none" + /// JP2K compression. + Jp2k = 8 // "jp2k" + } - /// Horizontal differencing. - Horizontal = 2, // "horizontal" + /// + /// The predictor can help deflate and lzw compression. + /// The values are fixed by the tiff library. + /// + public enum ForeignTiffPredictor + { + /// No prediction. + None = 1, // "none" - /// Float predictor. - Float = 3 // "float" - } + /// Horizontal differencing. + Horizontal = 2, // "horizontal" - /// - /// Use inches or centimeters as the resolution unit for a tiff file. - /// - public enum ForeignTiffResunit - { - /// Use centimeters. - Cm = 0, // "cm" + /// Float predictor. + Float = 3 // "float" + } - /// Use inches. - Inch = 1 // "inch" - } + /// + /// Use inches or centimeters as the resolution unit for a tiff file. + /// + public enum ForeignTiffResunit + { + /// Use centimeters. + Cm = 0, // "cm" - /// - /// Tune lossy encoder settings for different image types. - /// - public enum ForeignWebpPreset - { - /// Default preset. - Default = 0, // "default" + /// Use inches. + Inch = 1 // "inch" + } - /// Digital picture, like portrait, inner shot. - Picture = 1, // "picture" + /// + /// Tune lossy encoder settings for different image types. + /// + public enum ForeignWebpPreset + { + /// Default preset. + Default = 0, // "default" - /// Outdoor photograph, with natural lighting. - Photo = 2, // "photo" + /// Digital picture, like portrait, inner shot. + Picture = 1, // "picture" - /// Hand or line drawing, with high-contrast details. - Drawing = 3, // "drawing" + /// Outdoor photograph, with natural lighting. + Photo = 2, // "photo" - /// Small-sized colorful images/ - Icon = 4, // "icon" + /// Hand or line drawing, with high-contrast details. + Drawing = 3, // "drawing" - /// Text-like. - Text = 5 // "text" - } + /// Small-sized colorful images/ + Icon = 4, // "icon" - /// - /// The rendering intent. - /// - /// - /// See . - /// - public enum Intent - { - /// Perceptual rendering intent. - Perceptual = 0, // "perceptual" + /// Text-like. + Text = 5 // "text" + } - /// Relative colorimetric rendering intent. - Relative = 1, // "relative" + /// + /// The rendering intent. + /// + /// + /// See . + /// + public enum Intent + { + /// Perceptual rendering intent. + Perceptual = 0, // "perceptual" - /// Saturation rendering intent. - Saturation = 2, // "saturation" + /// Relative colorimetric rendering intent. + Relative = 1, // "relative" - /// Absolute colorimetric rendering intent. - Absolute = 3 // "absolute" - } + /// Saturation rendering intent. + Saturation = 2, // "saturation" - /// - /// Pick the algorithm vips uses to decide image "interestingness". - /// This is used by , for example, to decide what parts of the image to keep. - /// - public enum Interesting - { - /// Do nothing. - None = 0, // "none" + /// Absolute colorimetric rendering intent. + Absolute = 3 // "absolute" + } - /// Just take the centre. - Centre = 1, // "centre" + /// + /// Pick the algorithm vips uses to decide image "interestingness". + /// This is used by , for example, to decide what parts of the image to keep. + /// + public enum Interesting + { + /// Do nothing. + None = 0, // "none" - /// Use an entropy measure. - Entropy = 2, // "entropy" + /// Just take the centre. + Centre = 1, // "centre" - /// Look for features likely to draw human attention. - Attention = 3, // "attention" + /// Use an entropy measure. + Entropy = 2, // "entropy" - /// Position the crop towards the low coordinate. - Low = 4, // "low" + /// Look for features likely to draw human attention. + Attention = 3, // "attention" - /// Position the crop towards the high coordinate. - High = 5, // "high" + /// Position the crop towards the low coordinate. + Low = 4, // "low" - /// Everything is interesting. - All = 6 // "all" - } + /// Position the crop towards the high coordinate. + High = 5, // "high" - /// - /// How the values in an image should be interpreted. - /// - /// - /// For example, a three-band float image of type LAB should have its - /// pixels interpreted as coordinates in CIE Lab space. - /// - public enum Interpretation - { - /// Invalid setting. - Error = -1, // "error" + /// Everything is interesting. + All = 6 // "all" + } - /// Generic many-band image. - Multiband = 0, // "multiband" + /// + /// How the values in an image should be interpreted. + /// + /// + /// For example, a three-band float image of type LAB should have its + /// pixels interpreted as coordinates in CIE Lab space. + /// + public enum Interpretation + { + /// Invalid setting. + Error = -1, // "error" - /// Some kind of single-band image. - Bw = 1, // "b-w" + /// Generic many-band image. + Multiband = 0, // "multiband" - /// A 1D image, eg. histogram or lookup table. - Histogram = 10, // "histogram" + /// Some kind of single-band image. + Bw = 1, // "b-w" - /// The first three bands are CIE XYZ. - Xyz = 12, // "xyz" + /// A 1D image, eg. histogram or lookup table. + Histogram = 10, // "histogram" - /// Pixels are in CIE Lab space. - Lab = 13, // "lab" + /// The first three bands are CIE XYZ. + Xyz = 12, // "xyz" - /// The first four bands are in CMYK space. - Cmyk = 15, // "cmyk" + /// Pixels are in CIE Lab space. + Lab = 13, // "lab" - /// Implies . - Labq = 16, // "labq" + /// The first four bands are in CMYK space. + Cmyk = 15, // "cmyk" - /// Generic RGB space. - Rgb = 17, // "rgb" + /// Implies . + Labq = 16, // "labq" - /// A uniform colourspace based on CMC(1:1). - Cmc = 18, // "cmc" + /// Generic RGB space. + Rgb = 17, // "rgb" - /// Pixels are in CIE LCh space. - Lch = 19, // "lch" + /// A uniform colourspace based on CMC(1:1). + Cmc = 18, // "cmc" - /// CIE LAB coded as three signed 16-bit values. - Labs = 21, // "labs" + /// Pixels are in CIE LCh space. + Lch = 19, // "lch" - /// Pixels are sRGB. - Srgb = 22, // "srgb" + /// CIE LAB coded as three signed 16-bit values. + Labs = 21, // "labs" - /// Pixels are CIE Yxy. - Yxy = 23, // "yxy" + /// Pixels are sRGB. + Srgb = 22, // "srgb" - /// Image is in fourier space. - Fourier = 24, // "fourier" + /// Pixels are CIE Yxy. + Yxy = 23, // "yxy" - /// Generic 16-bit RGB. - Rgb16 = 25, // "rgb16" + /// Image is in fourier space. + Fourier = 24, // "fourier" - /// Generic 16-bit mono. - Grey16 = 26, // "grey16" + /// Generic 16-bit RGB. + Rgb16 = 25, // "rgb16" - /// A matrix. - Matrix = 27, // "matrix" + /// Generic 16-bit mono. + Grey16 = 26, // "grey16" - /// Pixels are scRGB. - Scrgb = 28, // "scrgb" + /// A matrix. + Matrix = 27, // "matrix" - /// Pixels are HSV. - Hsv = 29 // "hsv" - } + /// Pixels are scRGB. + Scrgb = 28, // "scrgb" - /// - /// A resizing kernel. One of these can be given to operations like - /// or to select the resizing kernel to use. - /// - public enum Kernel - { - /// Nearest-neighbour interpolation. - Nearest = 0, // "nearest" + /// Pixels are HSV. + Hsv = 29 // "hsv" + } - /// Linear interpolation. - Linear = 1, // "linear" + /// + /// A resizing kernel. One of these can be given to operations like + /// or to select the resizing kernel to use. + /// + public enum Kernel + { + /// Nearest-neighbour interpolation. + Nearest = 0, // "nearest" - /// Cubic interpolation. - Cubic = 2, // "cubic" + /// Linear interpolation. + Linear = 1, // "linear" - /// Mitchell - Mitchell = 3, // "mitchell" + /// Cubic interpolation. + Cubic = 2, // "cubic" - /// Two-lobe Lanczos. - Lanczos2 = 4, // "lanczos2" + /// Mitchell + Mitchell = 3, // "mitchell" - /// Three-lobe Lanczos. - Lanczos3 = 5 // "lanczos3" - } + /// Two-lobe Lanczos. + Lanczos2 = 4, // "lanczos2" - /// - /// Boolean operations. - /// - /// - /// See . - /// - public enum OperationBoolean - { - /// & - And = 0, // "and" + /// Three-lobe Lanczos. + Lanczos3 = 5 // "lanczos3" + } - /// | - Or = 1, // "or" + /// + /// Boolean operations. + /// + /// + /// See . + /// + public enum OperationBoolean + { + /// & + And = 0, // "and" - /// ^ - Eor = 2, // "eor" + /// | + Or = 1, // "or" - /// << - Lshift = 3, // "lshift" + /// ^ + Eor = 2, // "eor" - /// >> - Rshift = 4 // "rshift" - } + /// << + Lshift = 3, // "lshift" - /// - /// Operations on complex images. - /// - /// - /// See . - /// - public enum OperationComplex - { - /// Convert to polar coordinates. - Polar = 0, // "polar" + /// >> + Rshift = 4 // "rshift" + } - /// Convert to rectangular coordinates. - Rect = 1, // "rect" + /// + /// Operations on complex images. + /// + /// + /// See . + /// + public enum OperationComplex + { + /// Convert to polar coordinates. + Polar = 0, // "polar" - /// Complex conjugate. - Conj = 2 // "conj" - } + /// Convert to rectangular coordinates. + Rect = 1, // "rect" - /// - /// Binary operations on complex images. - /// - /// - /// See . - /// - public enum OperationComplex2 - { - /// Convert to polar coordinates. - CrossPhase = 0 // "cross-phase" - } + /// Complex conjugate. + Conj = 2 // "conj" + } - /// - /// Components of complex images. - /// - /// - /// See . - /// - public enum OperationComplexget - { - /// Get real component. - Real = 0, // "real" + /// + /// Binary operations on complex images. + /// + /// + /// See . + /// + public enum OperationComplex2 + { + /// Convert to polar coordinates. + CrossPhase = 0 // "cross-phase" + } - /// Get imaginary component. - Imag = 1 // "imag" - } + /// + /// Components of complex images. + /// + /// + /// See . + /// + public enum OperationComplexget + { + /// Get real component. + Real = 0, // "real" - /// - /// Various math functions on images. - /// - /// - /// See . - /// - public enum OperationMath - { - /// sin(), angles in degrees. - Sin = 0, // "sin" + /// Get imaginary component. + Imag = 1 // "imag" + } - /// cos(), angles in degrees. - Cos = 1, // "cos" + /// + /// Various math functions on images. + /// + /// + /// See . + /// + public enum OperationMath + { + /// sin(), angles in degrees. + Sin = 0, // "sin" - /// tan(), angles in degrees. - Tan = 2, // "tan" + /// cos(), angles in degrees. + Cos = 1, // "cos" - /// asin(), angles in degrees. - Asin = 3, // "asin" + /// tan(), angles in degrees. + Tan = 2, // "tan" - /// acos(), angles in degrees. - Acos = 4, // "acos" + /// asin(), angles in degrees. + Asin = 3, // "asin" - /// atan(), angles in degrees. - Atan = 5, // "atan" + /// acos(), angles in degrees. + Acos = 4, // "acos" - /// log base e. - Log = 6, // "log" + /// atan(), angles in degrees. + Atan = 5, // "atan" - /// log base 10. - Log10 = 7, // "log10" + /// log base e. + Log = 6, // "log" - /// e to the something. - Exp = 8, // "exp" + /// log base 10. + Log10 = 7, // "log10" - /// 10 to the something. - Exp10 = 9, // "exp10" + /// e to the something. + Exp = 8, // "exp" - /// sinh(), angles in radians. - Sinh = 10, // "sinh" + /// 10 to the something. + Exp10 = 9, // "exp10" - /// cosh(), angles in radians. - Cosh = 11, // "cosh" + /// sinh(), angles in radians. + Sinh = 10, // "sinh" - /// tanh(), angles in radians. - Tanh = 12, // "tanh" + /// cosh(), angles in radians. + Cosh = 11, // "cosh" - /// asinh(), angles in radians. - Asinh = 13, // "asinh" + /// tanh(), angles in radians. + Tanh = 12, // "tanh" - /// acosh(), angles in radians. - Acosh = 14, // "acosh" + /// asinh(), angles in radians. + Asinh = 13, // "asinh" - /// atanh(), angles in radians. - Atanh = 15 // "atanh" - } + /// acosh(), angles in radians. + Acosh = 14, // "acosh" - /// - /// Various math functions on images. - /// - /// - /// See . - /// - public enum OperationMath2 - { - /// pow( left, right ). - Pow = 0, // "pow" + /// atanh(), angles in radians. + Atanh = 15 // "atanh" + } - /// pow( right, left ). - Wop = 1, // "wop" + /// + /// Various math functions on images. + /// + /// + /// See . + /// + public enum OperationMath2 + { + /// pow( left, right ). + Pow = 0, // "pow" - /// atan2( left, right ) - Atan2 = 2 // "atan2" - } + /// pow( right, left ). + Wop = 1, // "wop" - /// - /// Morphological operations. - /// - /// - /// See . - /// - public enum OperationMorphology - { - /// true if all set. - Erode = 0, // "erode" + /// atan2( left, right ) + Atan2 = 2 // "atan2" + } - /// true if one set. - Dilate = 1 // "dilate" - } + /// + /// Morphological operations. + /// + /// + /// See . + /// + public enum OperationMorphology + { + /// true if all set. + Erode = 0, // "erode" - /// - /// Various relational operations. - /// - /// - /// See . - /// - public enum OperationRelational - { - /// == - Equal = 0, // "equal" + /// true if one set. + Dilate = 1 // "dilate" + } - /// != - Noteq = 1, // "noteq" + /// + /// Various relational operations. + /// + /// + /// See . + /// + public enum OperationRelational + { + /// == + Equal = 0, // "equal" - /// < - Less = 2, // "less" + /// != + Noteq = 1, // "noteq" - /// <= - Lesseq = 3, // "lesseq" + /// < + Less = 2, // "less" - /// > - More = 4, // "more" + /// <= + Lesseq = 3, // "lesseq" - /// >= - Moreeq = 5 // "moreeq" - } + /// > + More = 4, // "more" - /// - /// Round operations. - /// - public enum OperationRound - { - /// Round to nearest. - Rint = 0, // "rint" + /// >= + Moreeq = 5 // "moreeq" + } - /// The smallest integral value not less than. - Ceil = 1, // "ceil" + /// + /// Round operations. + /// + public enum OperationRound + { + /// Round to nearest. + Rint = 0, // "rint" - /// Largest integral value not greater than. - Floor = 2 // "floor" - } + /// The smallest integral value not less than. + Ceil = 1, // "ceil" - /// - /// Set Profile Connection Space. - /// - /// - /// See for example . - /// - public enum PCS - { - /// CIE Lab space. - Lab = 0, // "lab" + /// Largest integral value not greater than. + Floor = 2 // "floor" + } - /// CIE XYZ space. - Xyz = 1 // "xyz" - } + /// + /// Set Profile Connection Space. + /// + /// + /// See for example . + /// + public enum PCS + { + /// CIE Lab space. + Lab = 0, // "lab" - /// - /// Computation precision. - /// - /// - /// See for example . - /// - public enum Precision - { - /// Integer. - Integer = 0, // "integer" + /// CIE XYZ space. + Xyz = 1 // "xyz" + } - /// Floating point. - Float = 1, // "float" + /// + /// Computation precision. + /// + /// + /// See for example . + /// + public enum Precision + { + /// Integer. + Integer = 0, // "integer" - /// Compute approximate result. - Approximate = 2 // "approximate" - } + /// Floating point. + Float = 1, // "float" - /// - /// How to calculate the output pixels when shrinking a 2x2 region. - /// - public enum RegionShrink - { - /// Use the average. - Mean = 0, // "mean" + /// Compute approximate result. + Approximate = 2 // "approximate" + } - /// Use the median. - Median = 1, // "median" + /// + /// How to calculate the output pixels when shrinking a 2x2 region. + /// + public enum RegionShrink + { + /// Use the average. + Mean = 0, // "mean" - /// Use the mode. - Mode = 2, // "mode" + /// Use the median. + Median = 1, // "median" - /// Use the maximum. - Max = 3, // "max" + /// Use the mode. + Mode = 2, // "mode" - /// Use the minimum. - Min = 4, // "min" + /// Use the maximum. + Max = 3, // "max" - /// Use the top-left pixel. - Nearest = 5 // "nearest" - } + /// Use the minimum. + Min = 4, // "min" - /// - /// Controls whether an operation should upsize, downsize, both up and downsize, or force a size. - /// - /// - /// See for example . - /// - public enum Size - { - /// Size both up and down. - Both = 0, // "both" + /// Use the top-left pixel. + Nearest = 5 // "nearest" + } - /// Only upsize. - Up = 1, // "up" + /// + /// Controls whether an operation should upsize, downsize, both up and downsize, or force a size. + /// + /// + /// See for example . + /// + public enum Size + { + /// Size both up and down. + Both = 0, // "both" - /// Only downsize. - Down = 2, // "down" + /// Only upsize. + Up = 1, // "up" - /// Force size, that is, break aspect ratio. - Force = 3 // "force" - } + /// Only downsize. + Down = 2, // "down" - /// - /// Sets the word wrapping style for when used with a maximum width. - /// - public enum TextWrap - { - /// Wrap at word boundaries. - Word = 0, // "word" + /// Force size, that is, break aspect ratio. + Force = 3 // "force" + } - /// Wrap at character boundaries. - Char = 1, // "char" + /// + /// Sets the word wrapping style for when used with a maximum width. + /// + public enum TextWrap + { + /// Wrap at word boundaries. + Word = 0, // "word" - /// Wrap at word boundaries, but fall back to character boundaries if there is not enough space for a full word. - WordChar = 2, // "word-char" + /// Wrap at character boundaries. + Char = 1, // "char" - /// No wrapping. - None = 3 // "none" - } + /// Wrap at word boundaries, but fall back to character boundaries if there is not enough space for a full word. + WordChar = 2, // "word-char" - #endregion + /// No wrapping. + None = 3 // "none" + } - /// - /// Flags specifying the level of log messages. - /// - [Flags] - public enum LogLevelFlags - { - #region Internal log flags + #endregion - /// Internal flag. - FlagRecursion = 1 << 0, + /// + /// Flags specifying the level of log messages. + /// + [Flags] + public enum LogLevelFlags + { + #region Internal log flags - /// Internal flag. - FlagFatal = 1 << 1, + /// Internal flag. + FlagRecursion = 1 << 0, - #endregion + /// Internal flag. + FlagFatal = 1 << 1, - #region GLib log levels + #endregion - /// Log level for errors. - Error = 1 << 2, // always fatal + #region GLib log levels - /// Log level for critical warning messages. - Critical = 1 << 3, + /// Log level for errors. + Error = 1 << 2, // always fatal - /// Log level for warnings. - Warning = 1 << 4, + /// Log level for critical warning messages. + Critical = 1 << 3, - /// Log level for messages. - Message = 1 << 5, + /// Log level for warnings. + Warning = 1 << 4, - /// Log level for informational messages. - Info = 1 << 6, + /// Log level for messages. + Message = 1 << 5, - /// Log level for debug messages. - Debug = 1 << 7, + /// Log level for informational messages. + Info = 1 << 6, - #endregion + /// Log level for debug messages. + Debug = 1 << 7, - #region Convenience values + #endregion - /// All log levels except fatal. - AllButFatal = All & ~FlagFatal, + #region Convenience values - /// All log levels except recursion. - AllButRecursion = All & ~FlagRecursion, + /// All log levels except fatal. + AllButFatal = All & ~FlagFatal, - /// All log levels. - All = FlagMask | Error | Critical | Warning | Message | Info | Debug, + /// All log levels except recursion. + AllButRecursion = All & ~FlagRecursion, - /// Flag mask. - FlagMask = ~LevelMask, + /// All log levels. + All = FlagMask | Error | Critical | Warning | Message | Info | Debug, - /// A mask including all log levels. - LevelMask = ~(FlagRecursion | FlagFatal), + /// Flag mask. + FlagMask = ~LevelMask, - /// A mask with log levels that are considered fatal by default. - FatalMask = FlagRecursion | Error + /// A mask including all log levels. + LevelMask = ~(FlagRecursion | FlagFatal), - #endregion - } + /// A mask with log levels that are considered fatal by default. + FatalMask = FlagRecursion | Error - /// - /// Flags we associate with each object argument. - /// - [Flags] - public enum ArgumentFlags - { - /// No flags. - NONE = 0, + #endregion + } - /// Must be set in the constructor. - REQUIRED = 1 << 0, + /// + /// Flags we associate with each object argument. + /// + [Flags] + public enum ArgumentFlags + { + /// No flags. + NONE = 0, - /// Can only be set in the constructor. - CONSTRUCT = 1 << 1, + /// Must be set in the constructor. + REQUIRED = 1 << 0, - /// Can only be set once. - SET_ONCE = 1 << 2, + /// Can only be set in the constructor. + CONSTRUCT = 1 << 1, - /// Don't do use-before-set checks. - SET_ALWAYS = 1 << 3, + /// Can only be set once. + SET_ONCE = 1 << 2, - /// Is an input argument (one we depend on). - INPUT = 1 << 4, + /// Don't do use-before-set checks. + SET_ALWAYS = 1 << 3, - /// Is an output argument (depends on us). - OUTPUT = 1 << 5, + /// Is an input argument (one we depend on). + INPUT = 1 << 4, - /// Just there for back-compat, hide. - DEPRECATED = 1 << 6, + /// Is an output argument (depends on us). + OUTPUT = 1 << 5, - /// The input argument will be modified. - MODIFY = 1 << 7 - } + /// Just there for back-compat, hide. + DEPRECATED = 1 << 6, - /// - /// Flags we associate with an . - /// - [Flags] - public enum OperationFlags : uint - { - /// No flags. - NONE = 0, + /// The input argument will be modified. + MODIFY = 1 << 7 + } - /// Can work sequentially with a small buffer. - SEQUENTIAL = 1 << 0, + /// + /// Flags we associate with an . + /// + [Flags] + public enum OperationFlags : uint + { + /// No flags. + NONE = 0, - /// Can work sequentially without a buffer. - SEQUENTIAL_UNBUFFERED = 1 << 1, + /// Can work sequentially with a small buffer. + SEQUENTIAL = 1 << 0, - /// Must not be cached. - NOCACHE = 1 << 2, + /// Can work sequentially without a buffer. + SEQUENTIAL_UNBUFFERED = 1 << 1, - /// A compatibility thing. - DEPRECATED = 1 << 3, + /// Must not be cached. + NOCACHE = 1 << 2, - /// Not hardened for untrusted input. - UNTRUSTED = 1 << 4, + /// A compatibility thing. + DEPRECATED = 1 << 3, - /// Prevent this operation from running. - BLOCKED = 1 << 5 - } + /// Not hardened for untrusted input. + UNTRUSTED = 1 << 4, - /// - /// Flags we associate with a file load operation. - /// - [Flags] - public enum ForeignFlags : uint - { - /// No flags set. - NONE = 0, + /// Prevent this operation from running. + BLOCKED = 1 << 5 + } - /// Lazy read OK (eg. tiled tiff). - PARTIAL = 1 << 0, + /// + /// Flags we associate with a file load operation. + /// + [Flags] + public enum ForeignFlags : uint + { + /// No flags set. + NONE = 0, - /// Most-significant byte first. - BIGENDIAN = 1 << 1, + /// Lazy read OK (eg. tiled tiff). + PARTIAL = 1 << 0, - /// Top-to-bottom lazy read OK. - SEQUENTIAL = 1 << 2, + /// Most-significant byte first. + BIGENDIAN = 1 << 1, - /// All flags set. - ALL = 1 << 3 - } + /// Top-to-bottom lazy read OK. + SEQUENTIAL = 1 << 2, - /// - /// Signals that can be used on an . See . - /// - public enum Signals - { - /// Evaluation is starting. - /// - /// The preeval signal is emitted once before computation of - /// starts. It's a good place to set up evaluation feedback. - /// - PreEval = 0, // "preeval" - - /// Evaluation progress. - /// - /// The eval signal is emitted once per work unit (typically a 128 x - /// 128 area of pixels) during image computation. - /// - /// You can use this signal to update user-interfaces with progress - /// feedback. Beware of updating too frequently: you will usually - /// need some throttling mechanism. - /// - Eval = 1, // "eval" - - /// Evaluation is ending. - /// - /// The posteval signal is emitted once at the end of the computation - /// of . It's a good place to shut down evaluation feedback. - /// - PostEval = 2 // "posteval" - } + /// All flags set. + ALL = 1 << 3 + } + + /// + /// Signals that can be used on an . See . + /// + public enum Signals + { + /// Evaluation is starting. + /// + /// The preeval signal is emitted once before computation of + /// starts. It's a good place to set up evaluation feedback. + /// + PreEval = 0, // "preeval" + + /// Evaluation progress. + /// + /// The eval signal is emitted once per work unit (typically a 128 x + /// 128 area of pixels) during image computation. + /// + /// You can use this signal to update user-interfaces with progress + /// feedback. Beware of updating too frequently: you will usually + /// need some throttling mechanism. + /// + Eval = 1, // "eval" + + /// Evaluation is ending. + /// + /// The posteval signal is emitted once at the end of the computation + /// of . It's a good place to shut down evaluation feedback. + /// + PostEval = 2 // "posteval" } } \ No newline at end of file diff --git a/src/NetVips/ExtensionMethods.cs b/src/NetVips/ExtensionMethods.cs index d525c3a2..cb4479b2 100644 --- a/src/NetVips/ExtensionMethods.cs +++ b/src/NetVips/ExtensionMethods.cs @@ -1,290 +1,289 @@ -namespace NetVips -{ - using System; - using System.Buffers; - using System.Runtime.InteropServices; - using System.Text; - using Internal; +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 +{ /// - /// Useful extension methods that we use in our codebase. + /// Removes the element with the specified key from the + /// and retrieves the value to . /// - internal static class ExtensionMethods + /// 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) { - /// - /// 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); - } + 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) + /// + /// 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) { - foreach (var item in merge) - { - self[item.Key] = item.Value; - } + 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. + /// 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. + /// 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. + /// 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); + /// + /// 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) + /// + /// Prepends to . + /// + /// The array. + /// The to prepend to . + /// A new object array. + internal static object[] PrependImage(this T[] args, Image image) + { + if (args == null) { - if (args == null) - { - return new object[] { image }; - } + return new object[] { image }; + } - var newValues = new object[args.Length + 1]; - newValues[0] = image; - Array.Copy(args, 0, newValues, 1, args.Length); - return newValues; + 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; } - /// - /// 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 (size == 0) { - if (utf8Str == IntPtr.Zero) + while (Marshal.ReadByte(utf8Str, size) != 0) { - return null; + ++size; } + } - if (size == 0) + if (size == 0) + { + if (freePtr) { - while (Marshal.ReadByte(utf8Str, size) != 0) - { - ++size; - } + GLib.GFree(utf8Str); } - if (size == 0) - { - if (freePtr) - { - GLib.GFree(utf8Str); - } - - return string.Empty; - } + return string.Empty; + } - var bytes = ArrayPool.Shared.Rent(size); - try - { - Marshal.Copy(utf8Str, bytes, 0, size); - return Encoding.UTF8.GetString(bytes, 0, size); - } - finally + 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) { - ArrayPool.Shared.Return(bytes); - if (freePtr) - { - GLib.GFree(utf8Str); - } + 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) + /// + /// 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) { - string[] sizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; + dValue /= 1024; + i++; + } - var i = 0; - decimal dValue = value; - while (Math.Round(dValue, 2) >= 1000) - { - dValue /= 1024; - i++; - } + return $"{dValue:n2} {sizeSuffixes[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; } - /// - /// 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; + } - 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; } - /// - /// 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; + } - 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]; } - /// - /// 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; + } - 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]; } - /// - /// 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; + } - 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; } - /// - /// 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 (NetVips.AtLeastLibvips(8, 12)) { - 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); - } + 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) + /// + /// 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) { - if (!keep.HasValue) - { - return; - } + 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); - } + 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); } } } \ No newline at end of file diff --git a/src/NetVips/GObject.cs b/src/NetVips/GObject.cs index db51748b..788650eb 100644 --- a/src/NetVips/GObject.cs +++ b/src/NetVips/GObject.cs @@ -1,199 +1,199 @@ -namespace NetVips +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using NetVips.Internal; + +using GSignalMatchType = NetVips.Internal.Enums.GSignalMatchType; + +namespace NetVips; + +/// +/// Manage lifetime. +/// +public class GObject : SafeHandle { - using System; - using System.Collections.Generic; - using System.Runtime.InteropServices; - using Internal; - using GSignalMatchType = Internal.Enums.GSignalMatchType; + /// + /// We have to record all of the delegates to + /// prevent them from being re-located or disposed of by the garbage collector. + /// + /// + /// All recorded delegates are freed in . + /// + private readonly ICollection _handles = new List(); /// - /// Manage lifetime. + /// Hint of how much native memory is actually occupied by the object. /// - public class GObject : SafeHandle - { - /// - /// We have to record all of the delegates to - /// prevent them from being re-located or disposed of by the garbage collector. - /// - /// - /// All recorded delegates are freed in . - /// - private readonly ICollection _handles = new List(); - - /// - /// Hint of how much native memory is actually occupied by the object. - /// - internal long MemoryPressure; - - // Handy for debugging - // public static int NObjects; - - /// - /// Initializes a new instance of the class - /// with the specified pointer to wrap around. - /// - /// - /// Wraps a GObject instance around an underlying GValue. When the - /// instance is garbage-collected, the underlying object is unreferenced. - /// - /// The pointer to wrap around. - internal GObject(IntPtr pointer) - : base(IntPtr.Zero, true) - { - // record the pointer we were given to manage - SetHandle(pointer); + internal long MemoryPressure; - // NObjects++; - } + // Handy for debugging + // public static int NObjects; - /// - /// Connects a callback function () to a signal on this object. - /// - /// - /// The callback will be triggered every time this signal is issued on this instance. - /// - /// The type of the callback to connect. - /// A string of the form "signal-name::detail". - /// The callback to connect. - /// Data to pass to handler calls. - /// The handler id. - /// If it failed to connect the signal. - public ulong SignalConnect(string detailedSignal, T callback, nint data = default) - where T : notnull - { - // add a weak reference callback to ensure all handles are released on finalization - if (_handles.Count == 0) - { - GWeakNotify notify = ReleaseDelegates; - var notifyHandle = GCHandle.Alloc(notify); - - Internal.GObject.WeakRef(this, notify, GCHandle.ToIntPtr(notifyHandle)); - } + /// + /// Initializes a new instance of the class + /// with the specified pointer to wrap around. + /// + /// + /// Wraps a GObject instance around an underlying GValue. When the + /// instance is garbage-collected, the underlying object is unreferenced. + /// + /// The pointer to wrap around. + internal GObject(IntPtr pointer) + : base(IntPtr.Zero, true) + { + // record the pointer we were given to manage + SetHandle(pointer); - // prevent the delegate from being re-located or disposed of by the garbage collector - var delegateHandle = GCHandle.Alloc(callback); - _handles.Add(delegateHandle); + // NObjects++; + } - var cHandler = Marshal.GetFunctionPointerForDelegate(callback); - var ret = GSignal.ConnectData(this, detailedSignal, cHandler, data, null, default); - if (ret == 0) - { - throw new ArgumentException("Failed to connect signal " + detailedSignal); - } + /// + /// Connects a callback function () to a signal on this object. + /// + /// + /// The callback will be triggered every time this signal is issued on this instance. + /// + /// The type of the callback to connect. + /// A string of the form "signal-name::detail". + /// The callback to connect. + /// Data to pass to handler calls. + /// The handler id. + /// If it failed to connect the signal. + public ulong SignalConnect(string detailedSignal, T callback, nint data = default) + where T : notnull + { + // add a weak reference callback to ensure all handles are released on finalization + if (_handles.Count == 0) + { + GWeakNotify notify = ReleaseDelegates; + var notifyHandle = GCHandle.Alloc(notify); - return ret; + Internal.GObject.WeakRef(this, notify, GCHandle.ToIntPtr(notifyHandle)); } - /// - /// Disconnects a handler from this object. - /// - /// - /// If the is 0 then this function does nothing. - /// - /// Handler id of the handler to be disconnected. - public void SignalHandlerDisconnect(ulong handlerId) - { - if (handlerId != 0) - { - GSignal.HandlerDisconnect(this, handlerId); - } - } + // prevent the delegate from being re-located or disposed of by the garbage collector + var delegateHandle = GCHandle.Alloc(callback); + _handles.Add(delegateHandle); - /// - /// Disconnects all handlers from this object that match and - /// . - /// - /// The type of the func. - /// The func of the handlers. - /// The data of the handlers. - /// The number of handlers that matched. - public uint SignalHandlersDisconnectByFunc(T func, nint data = default) - where T : notnull + var cHandler = Marshal.GetFunctionPointerForDelegate(callback); + var ret = GSignal.ConnectData(this, detailedSignal, cHandler, data, null, default); + if (ret == 0) { - var funcPtr = Marshal.GetFunctionPointerForDelegate(func); - return GSignal.HandlersDisconnectMatched(this, - GSignalMatchType.G_SIGNAL_MATCH_FUNC | GSignalMatchType.G_SIGNAL_MATCH_DATA, - 0, 0, IntPtr.Zero, funcPtr, data); + throw new ArgumentException("Failed to connect signal " + detailedSignal); } - /// - /// Disconnects all handlers from this object that match . - /// - /// The data of the handlers. - /// The number of handlers that matched. - public uint SignalHandlersDisconnectByData(nint data) + return ret; + } + + /// + /// Disconnects a handler from this object. + /// + /// + /// If the is 0 then this function does nothing. + /// + /// Handler id of the handler to be disconnected. + public void SignalHandlerDisconnect(ulong handlerId) + { + if (handlerId != 0) { - return GSignal.HandlersDisconnectMatched(this, - GSignalMatchType.G_SIGNAL_MATCH_DATA, - 0, 0, IntPtr.Zero, IntPtr.Zero, data); + GSignal.HandlerDisconnect(this, handlerId); } + } - /// - /// Decreases the reference count of object. - /// When its reference count drops to 0, the object is finalized (i.e. its memory is freed). - /// - /// if the handle is released successfully; otherwise, - /// in the event of a catastrophic failure, . - protected override bool ReleaseHandle() - { - if (!IsInvalid) - { - Internal.GObject.Unref(handle); - } - // NObjects--; + /// + /// Disconnects all handlers from this object that match and + /// . + /// + /// The type of the func. + /// The func of the handlers. + /// The data of the handlers. + /// The number of handlers that matched. + public uint SignalHandlersDisconnectByFunc(T func, nint data = default) + where T : notnull + { + var funcPtr = Marshal.GetFunctionPointerForDelegate(func); + return GSignal.HandlersDisconnectMatched(this, + GSignalMatchType.G_SIGNAL_MATCH_FUNC | GSignalMatchType.G_SIGNAL_MATCH_DATA, + 0, 0, IntPtr.Zero, funcPtr, data); + } - return true; - } + /// + /// Disconnects all handlers from this object that match . + /// + /// The data of the handlers. + /// The number of handlers that matched. + public uint SignalHandlersDisconnectByData(nint data) + { + return GSignal.HandlersDisconnectMatched(this, + GSignalMatchType.G_SIGNAL_MATCH_DATA, + 0, 0, IntPtr.Zero, IntPtr.Zero, data); + } - /// - /// Release all the delegates by this object on finalization. - /// - /// - /// This function is only called when was used on this object. - /// - /// Data that was provided when the weak reference was established. - /// The object being disposed. - internal void ReleaseDelegates(nint data, nint objectPointer) + /// + /// Decreases the reference count of object. + /// When its reference count drops to 0, the object is finalized (i.e. its memory is freed). + /// + /// if the handle is released successfully; otherwise, + /// in the event of a catastrophic failure, . + protected override bool ReleaseHandle() + { + if (!IsInvalid) { - foreach (var gcHandle in _handles) - { - if (gcHandle.IsAllocated) - { - gcHandle.Free(); - } - } + Internal.GObject.Unref(handle); + } + // NObjects--; - // All GCHandles are free'd. Clear the list to prevent inadvertent use. - _handles.Clear(); + return true; + } - // Free the GCHandle used by this GWeakNotify - var notifyHandle = GCHandle.FromIntPtr(data); - if (notifyHandle.IsAllocated) + /// + /// Release all the delegates by this object on finalization. + /// + /// + /// This function is only called when was used on this object. + /// + /// Data that was provided when the weak reference was established. + /// The object being disposed. + internal void ReleaseDelegates(nint data, nint objectPointer) + { + foreach (var gcHandle in _handles) + { + if (gcHandle.IsAllocated) { - notifyHandle.Free(); + gcHandle.Free(); } } - /// - /// Increases the reference count of object. - /// - internal nint ObjectRef() + // All GCHandles are free'd. Clear the list to prevent inadvertent use. + _handles.Clear(); + + // Free the GCHandle used by this GWeakNotify + var notifyHandle = GCHandle.FromIntPtr(data); + if (notifyHandle.IsAllocated) { - return Internal.GObject.Ref(handle); + notifyHandle.Free(); } + } + + /// + /// Increases the reference count of object. + /// + internal nint ObjectRef() + { + return Internal.GObject.Ref(handle); + } - /// - /// Gets a value indicating whether the handle is invalid. - /// - /// if the handle is not valid; otherwise, . - public override bool IsInvalid => handle == IntPtr.Zero; + /// + /// Gets a value indicating whether the handle is invalid. + /// + /// if the handle is not valid; otherwise, . + public override bool IsInvalid => handle == IntPtr.Zero; - /// - /// Get the reference count of object. Handy for debugging. - /// - internal uint RefCount => Marshal.PtrToStructure(handle).RefCount; + /// + /// Get the reference count of object. Handy for debugging. + /// + internal uint RefCount => Marshal.PtrToStructure(handle).RefCount; - // Do not provide a finalizer - SafeHandle's critical finalizer will - // call ReleaseHandle for us. - } + // Do not provide a finalizer - SafeHandle's critical finalizer will + // call ReleaseHandle for us. } \ No newline at end of file diff --git a/src/NetVips/GValue.cs b/src/NetVips/GValue.cs index cb632a3e..74d83436 100644 --- a/src/NetVips/GValue.cs +++ b/src/NetVips/GValue.cs @@ -1,512 +1,513 @@ -namespace NetVips +using System; +using System.Collections; +using System.Runtime.InteropServices; +using System.Text; +using NetVips.Internal; + +namespace NetVips; + +/// +/// Wrap in a C# class. +/// +/// +/// This class wraps in a convenient interface. You can use +/// instances of this class to get and set properties. +/// +/// On construction, is all zero (empty). You can pass it to +/// a get function to have it filled by , or use init to +/// set a type, set to set a value, then use it to set an object property. +/// +/// GValue lifetime is managed automatically. +/// +public class GValue : IDisposable { - using System; - using System.Collections; - using System.Runtime.InteropServices; - using System.Text; - using Internal; + /// + /// The specified struct to wrap around. + /// + internal Internal.GValue.Struct Struct; + + /// + /// Track whether has been called. + /// + private bool _disposed; + + /// + /// Shift value used in converting numbers to type IDs. + /// + private const int FundamentalShift = 2; + + // look up some common gtypes at init for speed + + /// + /// The fundamental type corresponding to gboolean. + /// + public static readonly nint GBoolType = 5 << FundamentalShift; + + /// + /// The fundamental type corresponding to gint. + /// + public static readonly nint GIntType = 6 << FundamentalShift; + + /// + /// The fundamental type corresponding to guint64. + /// + public static readonly nint GUint64Type = 11 << FundamentalShift; + + /// + /// The fundamental type from which all enumeration types are derived. + /// + public static readonly nint GEnumType = 12 << FundamentalShift; + + /// + /// The fundamental type from which all flags types are derived. + /// + public static readonly nint GFlagsType = 13 << FundamentalShift; + + /// + /// The fundamental type corresponding to gdouble. + /// + public static readonly nint GDoubleType = 15 << FundamentalShift; + + /// + /// The fundamental type corresponding to null-terminated C strings. + /// + public static readonly nint GStrType = 16 << FundamentalShift; + + /// + /// The fundamental type for GObject. + /// + public static readonly nint GObjectType = 20 << FundamentalShift; /// - /// Wrap in a C# class. + /// The fundamental type for VipsImage. + /// + public static readonly nint ImageType = NetVips.TypeFromName("VipsImage"); + + /// + /// The fundamental type for VipsArrayInt. + /// + public static readonly nint ArrayIntType = NetVips.TypeFromName("VipsArrayInt"); + + /// + /// The fundamental type for VipsArrayDouble. + /// + public static readonly nint ArrayDoubleType = NetVips.TypeFromName("VipsArrayDouble"); + + /// + /// The fundamental type for VipsArrayImage. + /// + public static readonly nint ArrayImageType = NetVips.TypeFromName("VipsArrayImage"); + + /// + /// The fundamental type for VipsRefString. + /// + public static readonly nint RefStrType = NetVips.TypeFromName("VipsRefString"); + + /// + /// The fundamental type for VipsBlob. + /// + public static readonly nint BlobType = NetVips.TypeFromName("VipsBlob"); + + /// + /// The fundamental type for VipsBlendMode. See . + /// + public static readonly nint BlendModeType; + + /// + /// The fundamental type for VipsSource. See . + /// + public static readonly nint SourceType; + + /// + /// The fundamental type for VipsTarget. See . + /// + public static readonly nint TargetType; + + /// + /// Hint of how much native memory is actually occupied by the object. + /// + private long _memoryPressure; + + static GValue() + { + if (NetVips.AtLeastLibvips(8, 6)) + { + BlendModeType = Vips.BlendModeGetType(); + } + + if (NetVips.AtLeastLibvips(8, 9)) + { + SourceType = NetVips.TypeFromName("VipsSource"); + TargetType = NetVips.TypeFromName("VipsTarget"); + } + } + + /// + /// Initializes a new instance of the class. + /// + public GValue() + { + Struct = new Internal.GValue.Struct(); + } + + /// + /// Initializes a new instance of the class + /// with the specified struct to wrap around. + /// + /// The specified struct to wrap around. + internal GValue(Internal.GValue.Struct value) + { + Struct = value; + } + + /// + /// Set the type of a GValue. /// /// - /// This class wraps in a convenient interface. You can use - /// instances of this class to get and set properties. + /// GValues have a set type, fixed at creation time. Use SetType to set + /// the type of a GValue before assigning to it. /// - /// On construction, is all zero (empty). You can pass it to - /// a get function to have it filled by , or use init to - /// set a type, set to set a value, then use it to set an object property. - /// - /// GValue lifetime is managed automatically. + /// GTypes are 32 or 64-bit integers (depending on the platform). See + /// TypeFind. /// - public class GValue : IDisposable + /// Type the GValue should hold values of. + public void SetType(nint gtype) { - /// - /// The specified struct to wrap around. - /// - internal Internal.GValue.Struct Struct; - - /// - /// Track whether has been called. - /// - private bool _disposed; - - /// - /// Shift value used in converting numbers to type IDs. - /// - private const int FundamentalShift = 2; - - // look up some common gtypes at init for speed - - /// - /// The fundamental type corresponding to gboolean. - /// - public static readonly nint GBoolType = 5 << FundamentalShift; - - /// - /// The fundamental type corresponding to gint. - /// - public static readonly nint GIntType = 6 << FundamentalShift; - - /// - /// The fundamental type corresponding to guint64. - /// - public static readonly nint GUint64Type = 11 << FundamentalShift; - - /// - /// The fundamental type from which all enumeration types are derived. - /// - public static readonly nint GEnumType = 12 << FundamentalShift; - - /// - /// The fundamental type from which all flags types are derived. - /// - public static readonly nint GFlagsType = 13 << FundamentalShift; - - /// - /// The fundamental type corresponding to gdouble. - /// - public static readonly nint GDoubleType = 15 << FundamentalShift; - - /// - /// The fundamental type corresponding to null-terminated C strings. - /// - public static readonly nint GStrType = 16 << FundamentalShift; - - /// - /// The fundamental type for GObject. - /// - public static readonly nint GObjectType = 20 << FundamentalShift; - - /// - /// The fundamental type for VipsImage. - /// - public static readonly nint ImageType = NetVips.TypeFromName("VipsImage"); - - /// - /// The fundamental type for VipsArrayInt. - /// - public static readonly nint ArrayIntType = NetVips.TypeFromName("VipsArrayInt"); - - /// - /// The fundamental type for VipsArrayDouble. - /// - public static readonly nint ArrayDoubleType = NetVips.TypeFromName("VipsArrayDouble"); - - /// - /// The fundamental type for VipsArrayImage. - /// - public static readonly nint ArrayImageType = NetVips.TypeFromName("VipsArrayImage"); - - /// - /// The fundamental type for VipsRefString. - /// - public static readonly nint RefStrType = NetVips.TypeFromName("VipsRefString"); - - /// - /// The fundamental type for VipsBlob. - /// - public static readonly nint BlobType = NetVips.TypeFromName("VipsBlob"); - - /// - /// The fundamental type for VipsBlendMode. See . - /// - public static readonly nint BlendModeType; - - /// - /// The fundamental type for VipsSource. See . - /// - public static readonly nint SourceType; - - /// - /// The fundamental type for VipsTarget. See . - /// - public static readonly nint TargetType; - - /// - /// Hint of how much native memory is actually occupied by the object. - /// - private long _memoryPressure; - - static GValue() - { - if (NetVips.AtLeastLibvips(8, 6)) - { - BlendModeType = Vips.BlendModeGetType(); - } + Internal.GValue.Init(ref Struct, gtype); + } - if (NetVips.AtLeastLibvips(8, 9)) - { - SourceType = NetVips.TypeFromName("VipsSource"); - TargetType = NetVips.TypeFromName("VipsTarget"); - } + /// + /// Ensure that the GC knows the true cost of the object during collection. + /// + /// + /// If the object is actually bigger than the managed size reflects, it may + /// be a candidate for quick(er) collection. + /// + /// The amount of unmanaged memory that has been allocated. + private void AddMemoryPressure(long bytesAllocated) + { + if (bytesAllocated <= 0) + { + return; } - /// - /// Initializes a new instance of the class. - /// - public GValue() + GC.AddMemoryPressure(bytesAllocated); + _memoryPressure += bytesAllocated; + } + + /// + /// Set a GValue. + /// + /// + /// The value is converted to the type of the GValue, if possible, and + /// assigned. + /// + /// Value to be set. + public void Set(object value) + { + var gtype = GetTypeOf(); + var fundamental = GType.Fundamental(gtype); + if (gtype == GBoolType) { - Struct = new Internal.GValue.Struct(); + Internal.GValue.SetBoolean(ref Struct, Convert.ToBoolean(value)); } - - /// - /// Initializes a new instance of the class - /// with the specified struct to wrap around. - /// - /// The specified struct to wrap around. - internal GValue(Internal.GValue.Struct value) + else if (gtype == GIntType) { - Struct = value; + Internal.GValue.SetInt(ref Struct, Convert.ToInt32(value)); } - - /// - /// Set the type of a GValue. - /// - /// - /// GValues have a set type, fixed at creation time. Use SetType to set - /// the type of a GValue before assigning to it. - /// - /// GTypes are 32 or 64-bit integers (depending on the platform). See - /// TypeFind. - /// - /// Type the GValue should hold values of. - public void SetType(nint gtype) + else if (gtype == GUint64Type) { - Internal.GValue.Init(ref Struct, gtype); + Internal.GValue.SetUint64(ref Struct, Convert.ToUInt64(value)); } - - /// - /// Ensure that the GC knows the true cost of the object during collection. - /// - /// - /// If the object is actually bigger than the managed size reflects, it may - /// be a candidate for quick(er) collection. - /// - /// The amount of unmanaged memory that has been allocated. - private void AddMemoryPressure(long bytesAllocated) + else if (gtype == GDoubleType) { - if (bytesAllocated <= 0) - { - return; - } - - GC.AddMemoryPressure(bytesAllocated); - _memoryPressure += bytesAllocated; + Internal.GValue.SetDouble(ref Struct, Convert.ToDouble(value)); } - - /// - /// Set a GValue. - /// - /// - /// The value is converted to the type of the GValue, if possible, and - /// assigned. - /// - /// Value to be set. - public void Set(object value) + else if (fundamental == GEnumType) { - var gtype = GetTypeOf(); - var fundamental = GType.Fundamental(gtype); - if (gtype == GBoolType) - { - Internal.GValue.SetBoolean(ref Struct, Convert.ToBoolean(value)); - } - else if (gtype == GIntType) - { - Internal.GValue.SetInt(ref Struct, Convert.ToInt32(value)); - } - else if (gtype == GUint64Type) - { - Internal.GValue.SetUint64(ref Struct, Convert.ToUInt64(value)); - } - else if (gtype == GDoubleType) - { - Internal.GValue.SetDouble(ref Struct, Convert.ToDouble(value)); - } - else if (fundamental == GEnumType) - { - Internal.GValue.SetEnum(ref Struct, Convert.ToInt32(value)); - } - else if (fundamental == GFlagsType) - { - Internal.GValue.SetFlags(ref Struct, Convert.ToUInt32(value)); - } - else if (gtype == GStrType) + Internal.GValue.SetEnum(ref Struct, Convert.ToInt32(value)); + } + else if (fundamental == GFlagsType) + { + Internal.GValue.SetFlags(ref Struct, Convert.ToUInt32(value)); + } + else if (gtype == GStrType) + { + var bytes = Encoding.UTF8.GetBytes(Convert.ToString(value) + + char.MinValue); // Ensure null-terminated string + Internal.GValue.SetString(ref Struct, bytes); + } + else if (gtype == RefStrType) + { + var bytes = Encoding.UTF8.GetBytes(Convert.ToString(value) + + char.MinValue); // Ensure null-terminated string + VipsValue.SetRefString(ref Struct, bytes); + } + else if (fundamental == GObjectType && value is GObject gObject) + { + AddMemoryPressure(gObject.MemoryPressure); + Internal.GValue.SetObject(ref Struct, gObject); + } + else if (gtype == ArrayIntType) + { + if (value is not IEnumerable) { - var bytes = Encoding.UTF8.GetBytes(Convert.ToString(value) + char.MinValue); // Ensure null-terminated string - Internal.GValue.SetString(ref Struct, bytes); + value = new[] { value }; } - else if (gtype == RefStrType) + + var integers = value switch { - var bytes = Encoding.UTF8.GetBytes(Convert.ToString(value) + char.MinValue); // Ensure null-terminated string - VipsValue.SetRefString(ref Struct, bytes); - } - else if (fundamental == GObjectType && value is GObject gObject) + int[] ints => ints, + double[] doubles => Array.ConvertAll(doubles, Convert.ToInt32), + object[] objects => Array.ConvertAll(objects, Convert.ToInt32), + _ => throw new ArgumentException( + $"unsupported value type {value.GetType()} for gtype {NetVips.TypeName(gtype)}") + }; + + VipsValue.SetArrayInt(ref Struct, integers, integers.Length); + } + else if (gtype == ArrayDoubleType) + { + if (value is not IEnumerable) { - AddMemoryPressure(gObject.MemoryPressure); - Internal.GValue.SetObject(ref Struct, gObject); + value = new[] { value }; } - else if (gtype == ArrayIntType) + + var doubles = value switch { - if (value is not IEnumerable) - { - value = new[] { value }; - } + double[] dbls => dbls, + int[] ints => Array.ConvertAll(ints, Convert.ToDouble), + object[] objects => Array.ConvertAll(objects, Convert.ToDouble), + _ => throw new ArgumentException( + $"unsupported value type {value.GetType()} for gtype {NetVips.TypeName(gtype)}") + }; - var integers = value switch - { - int[] ints => ints, - double[] doubles => Array.ConvertAll(doubles, Convert.ToInt32), - object[] objects => Array.ConvertAll(objects, Convert.ToInt32), - _ => throw new ArgumentException( - $"unsupported value type {value.GetType()} for gtype {NetVips.TypeName(gtype)}") - }; - - VipsValue.SetArrayInt(ref Struct, integers, integers.Length); - } - else if (gtype == ArrayDoubleType) + VipsValue.SetArrayDouble(ref Struct, doubles, doubles.Length); + } + else if (gtype == ArrayImageType && value is Image[] images) + { + var size = images.Length; + VipsValue.SetArrayImage(ref Struct, size); + + var ptrArr = VipsValue.GetArrayImage(in Struct, out _); + + for (var i = 0; i < size; i++) { - if (value is not IEnumerable) - { - value = new[] { value }; - } + ref var image = ref images[i]; - var doubles = value switch - { - double[] dbls => dbls, - int[] ints => Array.ConvertAll(ints, Convert.ToDouble), - object[] objects => Array.ConvertAll(objects, Convert.ToDouble), - _ => throw new ArgumentException( - $"unsupported value type {value.GetType()} for gtype {NetVips.TypeName(gtype)}") - }; - - VipsValue.SetArrayDouble(ref Struct, doubles, doubles.Length); + // the gvalue needs a ref on each of the images + Marshal.WriteIntPtr(ptrArr, i * IntPtr.Size, image.ObjectRef()); + + AddMemoryPressure(image.MemoryPressure); } - else if (gtype == ArrayImageType && value is Image[] images) + } + else if (gtype == BlobType && value is VipsBlob blob) + { + AddMemoryPressure((long)blob.Length); + Internal.GValue.SetBoxed(ref Struct, blob); + } + else if (gtype == BlobType) + { + var memory = value switch { - var size = images.Length; - VipsValue.SetArrayImage(ref Struct, size); - - var ptrArr = VipsValue.GetArrayImage(in Struct, out _); + string strValue => Encoding.UTF8.GetBytes(strValue), + char[] charArrValue => Encoding.UTF8.GetBytes(charArrValue), + byte[] byteArrValue => byteArrValue, + _ => throw new ArgumentException( + $"unsupported value type {value.GetType()} for gtype {NetVips.TypeName(gtype)}") + }; - for (var i = 0; i < size; i++) - { - ref var image = ref images[i]; + // We need to set the blob to a copy of the string that vips can own + var ptr = GLib.GMalloc((ulong)memory.Length); + Marshal.Copy(memory, 0, ptr, memory.Length); - // the gvalue needs a ref on each of the images - Marshal.WriteIntPtr(ptrArr, i * IntPtr.Size, image.ObjectRef()); + AddMemoryPressure(memory.Length); - AddMemoryPressure(image.MemoryPressure); - } - } - else if (gtype == BlobType && value is VipsBlob blob) + if (NetVips.AtLeastLibvips(8, 6)) { - AddMemoryPressure((long)blob.Length); - Internal.GValue.SetBoxed(ref Struct, blob); + VipsValue.SetBlobFree(ref Struct, ptr, (ulong)memory.Length); } - else if (gtype == BlobType) + else { - var memory = value switch + int FreeFn(nint a, nint b) { - string strValue => Encoding.UTF8.GetBytes(strValue), - char[] charArrValue => Encoding.UTF8.GetBytes(charArrValue), - byte[] byteArrValue => byteArrValue, - _ => throw new ArgumentException( - $"unsupported value type {value.GetType()} for gtype {NetVips.TypeName(gtype)}") - }; - - // We need to set the blob to a copy of the string that vips can own - var ptr = GLib.GMalloc((ulong)memory.Length); - Marshal.Copy(memory, 0, ptr, memory.Length); + GLib.GFree(a); - AddMemoryPressure(memory.Length); - - if (NetVips.AtLeastLibvips(8, 6)) - { - VipsValue.SetBlobFree(ref Struct, ptr, (ulong)memory.Length); + return 0; } - else - { - int FreeFn(nint a, nint b) - { - GLib.GFree(a); - - return 0; - } - VipsValue.SetBlob(ref Struct, FreeFn, ptr, (ulong)memory.Length); - } - } - else - { - throw new ArgumentException( - $"unsupported gtype for set {NetVips.TypeName(gtype)}, fundamental {NetVips.TypeName(fundamental)}, value type {value.GetType()}"); + VipsValue.SetBlob(ref Struct, FreeFn, ptr, (ulong)memory.Length); } } - - /// - /// Get the contents of a GValue. - /// - /// - /// The contents of the GValue are read out as a C# type. - /// - /// The contents of this GValue. - public object Get() + else { - var gtype = GetTypeOf(); - var fundamental = GType.Fundamental(gtype); + throw new ArgumentException( + $"unsupported gtype for set {NetVips.TypeName(gtype)}, fundamental {NetVips.TypeName(fundamental)}, value type {value.GetType()}"); + } + } - object result; - if (gtype == GBoolType) - { - result = Internal.GValue.GetBoolean(in Struct); - } - else if (gtype == GIntType) - { - result = Internal.GValue.GetInt(in Struct); - } - else if (gtype == GUint64Type) - { - result = Internal.GValue.GetUint64(in Struct); - } - else if (gtype == GDoubleType) - { - result = Internal.GValue.GetDouble(in Struct); - } - else if (fundamental == GEnumType) - { - result = Internal.GValue.GetEnum(in Struct); - } - else if (fundamental == GFlagsType) - { - result = Internal.GValue.GetFlags(in Struct); - } - else if (gtype == GStrType) - { - result = Internal.GValue.GetString(in Struct).ToUtf8String(); - } - else if (gtype == RefStrType) - { - result = VipsValue.GetRefString(in Struct, out var size).ToUtf8String(size: (int)size); - } - else if (gtype == ImageType) - { - // g_value_get_object() will not add a ref ... that is - // held by the gvalue - var vi = Internal.GValue.GetObject(in Struct); - - // we want a ref that will last with the life of the vimage: - // this ref is matched by the unref that's attached to finalize - // by GObject - var image = new Image(vi); - image.ObjectRef(); + /// + /// Get the contents of a GValue. + /// + /// + /// The contents of the GValue are read out as a C# type. + /// + /// The contents of this GValue. + public object Get() + { + var gtype = GetTypeOf(); + var fundamental = GType.Fundamental(gtype); - result = image; - } - else if (gtype == ArrayIntType) - { - var intPtr = VipsValue.GetArrayInt(in Struct, out var size); + object result; + if (gtype == GBoolType) + { + result = Internal.GValue.GetBoolean(in Struct); + } + else if (gtype == GIntType) + { + result = Internal.GValue.GetInt(in Struct); + } + else if (gtype == GUint64Type) + { + result = Internal.GValue.GetUint64(in Struct); + } + else if (gtype == GDoubleType) + { + result = Internal.GValue.GetDouble(in Struct); + } + else if (fundamental == GEnumType) + { + result = Internal.GValue.GetEnum(in Struct); + } + else if (fundamental == GFlagsType) + { + result = Internal.GValue.GetFlags(in Struct); + } + else if (gtype == GStrType) + { + result = Internal.GValue.GetString(in Struct).ToUtf8String(); + } + else if (gtype == RefStrType) + { + result = VipsValue.GetRefString(in Struct, out var size).ToUtf8String(size: (int)size); + } + else if (gtype == ImageType) + { + // g_value_get_object() will not add a ref ... that is + // held by the gvalue + var vi = Internal.GValue.GetObject(in Struct); - var intArr = new int[size]; - Marshal.Copy(intPtr, intArr, 0, size); - result = intArr; - } - else if (gtype == ArrayDoubleType) - { - var intPtr = VipsValue.GetArrayDouble(in Struct, out var size); + // we want a ref that will last with the life of the vimage: + // this ref is matched by the unref that's attached to finalize + // by GObject + var image = new Image(vi); + image.ObjectRef(); - var doubleArr = new double[size]; - Marshal.Copy(intPtr, doubleArr, 0, size); - result = doubleArr; - } - else if (gtype == ArrayImageType) - { - var ptrArr = VipsValue.GetArrayImage(in Struct, out var size); + result = image; + } + else if (gtype == ArrayIntType) + { + var intPtr = VipsValue.GetArrayInt(in Struct, out var size); - var images = new Image[size]; - for (var i = 0; i < size; i++) - { - var vi = Marshal.ReadIntPtr(ptrArr, i * IntPtr.Size); - ref var image = ref images[i]; - image = new Image(vi); - image.ObjectRef(); - } + var intArr = new int[size]; + Marshal.Copy(intPtr, intArr, 0, size); + result = intArr; + } + else if (gtype == ArrayDoubleType) + { + var intPtr = VipsValue.GetArrayDouble(in Struct, out var size); - result = images; - } - else if (gtype == BlobType) - { - var array = VipsValue.GetBlob(in Struct, out var size); + var doubleArr = new double[size]; + Marshal.Copy(intPtr, doubleArr, 0, size); + result = doubleArr; + } + else if (gtype == ArrayImageType) + { + var ptrArr = VipsValue.GetArrayImage(in Struct, out var size); - // Blob types are returned as an array of bytes. - var byteArr = new byte[size]; - Marshal.Copy(array, byteArr, 0, (int)size); - result = byteArr; - } - else + var images = new Image[size]; + for (var i = 0; i < size; i++) { - throw new ArgumentException($"unsupported gtype for get {NetVips.TypeName(gtype)}"); + var vi = Marshal.ReadIntPtr(ptrArr, i * IntPtr.Size); + ref var image = ref images[i]; + image = new Image(vi); + image.ObjectRef(); } - return result; + result = images; } - - /// - /// Get the GType of this GValue. - /// - /// The GType of this GValue. - public nint GetTypeOf() + else if (gtype == BlobType) { - return Struct.GType; - } + var array = VipsValue.GetBlob(in Struct, out var size); - /// - /// Finalizes an instance of the class. - /// - /// - /// Allows an object to try to free resources and perform other cleanup - /// operations before it is reclaimed by garbage collection. - /// - ~GValue() + // Blob types are returned as an array of bytes. + var byteArr = new byte[size]; + Marshal.Copy(array, byteArr, 0, (int)size); + result = byteArr; + } + else { - // Do not re-create Dispose clean-up code here. - Dispose(false); + throw new ArgumentException($"unsupported gtype for get {NetVips.TypeName(gtype)}"); } - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// to release both managed and unmanaged resources; - /// to release only unmanaged resources. - protected void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!_disposed) - { - // and tag it to be unset on GC as well - Internal.GValue.Unset(ref Struct); + return result; + } - if (_memoryPressure > 0) - { - GC.RemoveMemoryPressure(_memoryPressure); - _memoryPressure = 0; - } + /// + /// Get the GType of this GValue. + /// + /// The GType of this GValue. + public nint GetTypeOf() + { + return Struct.GType; + } - // Note disposing has been done. - _disposed = true; - } - } + /// + /// Finalizes an instance of the class. + /// + /// + /// Allows an object to try to free resources and perform other cleanup + /// operations before it is reclaimed by garbage collection. + /// + ~GValue() + { + // Do not re-create Dispose clean-up code here. + Dispose(false); + } - /// - /// Performs application-defined tasks associated with freeing, releasing, - /// or resetting unmanaged resources. - /// - public void Dispose() + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + protected void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!_disposed) { - Dispose(true); + // and tag it to be unset on GC as well + Internal.GValue.Unset(ref Struct); + + if (_memoryPressure > 0) + { + GC.RemoveMemoryPressure(_memoryPressure); + _memoryPressure = 0; + } - // This object will be cleaned up by the Dispose method. - GC.SuppressFinalize(this); + // Note disposing has been done. + _disposed = true; } } + + /// + /// Performs application-defined tasks associated with freeing, releasing, + /// or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + + // This object will be cleaned up by the Dispose method. + GC.SuppressFinalize(this); + } } \ No newline at end of file diff --git a/src/NetVips/Image.Generated.cs b/src/NetVips/Image.Generated.cs index bc69eebb..3a065d53 100644 --- a/src/NetVips/Image.Generated.cs +++ b/src/NetVips/Image.Generated.cs @@ -8,11610 +8,11609 @@ // //------------------------------------------------------------------------------ -namespace NetVips +using System.IO; + +namespace NetVips; + +public partial class Image { - using System.IO; - - public partial class Image - { - #region auto-generated functions - - /// - /// Absolute value of an image. - /// - /// - /// - /// using Image @out = in.Abs(); - /// - /// - /// A new . - public Image Abs() - { - return this.Call("abs") as Image; - } - - /// - /// Add two images. - /// - /// - /// - /// using Image @out = left.Add(right); - /// - /// - /// Right-hand image argument. - /// A new . - public Image Add(Image right) - { - return this.Call("add", right) as Image; - } - - /// - /// Affine transform of an image. - /// - /// - /// - /// using Image @out = in.Affine(matrix, interpolate: GObject, oarea: int[], odx: double, ody: double, idx: double, idy: double, background: double[], premultiplied: bool, extend: Enums.Extend); - /// - /// - /// Transformation matrix. - /// Interpolate pixels with this. - /// Area of output to generate. - /// Horizontal output displacement. - /// Vertical output displacement. - /// Horizontal input displacement. - /// Vertical input displacement. - /// Background value. - /// Images have premultiplied alpha. - /// How to generate the extra pixels. - /// A new . - public Image Affine(double[] matrix, GObject interpolate = null, int[] oarea = null, double? odx = null, double? ody = null, double? idx = null, double? idy = null, double[] background = null, bool? premultiplied = null, Enums.Extend? extend = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(interpolate), interpolate); - options.AddIfPresent(nameof(oarea), oarea); - options.AddIfPresent(nameof(odx), odx); - options.AddIfPresent(nameof(ody), ody); - options.AddIfPresent(nameof(idx), idx); - options.AddIfPresent(nameof(idy), idy); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent(nameof(premultiplied), premultiplied); - options.AddIfPresent(nameof(extend), extend); - - return this.Call("affine", options, matrix) as Image; - } - - /// - /// Load an Analyze6 image. - /// - /// - /// - /// using Image @out = NetVips.Image.Analyzeload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Analyzeload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("analyzeload", options, filename) as Image; - } - - /// - /// Load an Analyze6 image. - /// - /// - /// - /// using Image @out = NetVips.Image.Analyzeload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Analyzeload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("analyzeload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Join an array of images. - /// - /// - /// - /// using Image @out = NetVips.Image.Arrayjoin(@in, across: int, shim: int, background: double[], halign: Enums.Align, valign: Enums.Align, hspacing: int, vspacing: int); - /// - /// - /// Array of input images. - /// Number of images across grid. - /// Pixels between images. - /// Colour for new pixels. - /// Align on the left, centre or right. - /// Align on the top, centre or bottom. - /// Horizontal spacing between images. - /// Vertical spacing between images. - /// A new . - public static Image Arrayjoin(Image[] @in, int? across = null, int? shim = null, double[] background = null, Enums.Align? halign = null, Enums.Align? valign = null, int? hspacing = null, int? vspacing = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(across), across); - options.AddIfPresent(nameof(shim), shim); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent(nameof(halign), halign); - options.AddIfPresent(nameof(valign), valign); - options.AddIfPresent(nameof(hspacing), hspacing); - options.AddIfPresent(nameof(vspacing), vspacing); - - return Operation.Call("arrayjoin", options, new object[] { @in }) as Image; - } - - /// - /// Autorotate image by exif tag. - /// - /// - /// - /// using Image @out = in.Autorot(); - /// - /// - /// A new . - public Image Autorot() - { - return this.Call("autorot") as Image; - } - - /// - /// Autorotate image by exif tag. - /// - /// - /// - /// using Image @out = in.Autorot(out var angle); - /// - /// - /// Angle image was rotated by. - /// A new . - public Image Autorot(out Enums.Angle angle) - { - var optionalOutput = new VOption - { - {"angle", true} - }; - - var results = this.Call("autorot", optionalOutput) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - angle = (Enums.Angle)opts?["angle"]; - - return finalResult; - } - - /// - /// Autorotate image by exif tag. - /// - /// - /// - /// using Image @out = in.Autorot(out var angle, out var flip); - /// - /// - /// Angle image was rotated by. - /// Whether the image was flipped or not. - /// A new . - public Image Autorot(out Enums.Angle angle, out bool flip) - { - var optionalOutput = new VOption - { - {"angle", true}, - {"flip", true} - }; - - var results = this.Call("autorot", optionalOutput) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - angle = (Enums.Angle)opts?["angle"]; - flip = opts?["flip"] is bool out2 && out2; - - return finalResult; - } - - /// - /// Find image average. - /// - /// - /// - /// double @out = in.Avg(); - /// - /// - /// A double. - public double Avg() - { - return this.Call("avg") is double result ? result : 0d; - } - - /// - /// Boolean operation across image bands. - /// - /// - /// - /// using Image @out = in.Bandbool(boolean); - /// - /// - /// Boolean to perform. - /// A new . - public Image Bandbool(Enums.OperationBoolean boolean) - { - return this.Call("bandbool", boolean) as Image; - } - - /// - /// Fold up x axis into bands. - /// - /// - /// - /// using Image @out = in.Bandfold(factor: int); - /// - /// - /// Fold by this factor. - /// A new . - public Image Bandfold(int? factor = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(factor), factor); - - return this.Call("bandfold", options) as Image; - } - - /// - /// Append a constant band to an image. - /// - /// - /// - /// using Image @out = in.BandjoinConst(c); - /// - /// - /// Array of constants to add. - /// A new . - public Image BandjoinConst(double[] c) - { - return this.Call("bandjoin_const", c) as Image; - } - - /// - /// Band-wise average. - /// - /// - /// - /// using Image @out = in.Bandmean(); - /// - /// - /// A new . - public Image Bandmean() - { - return this.Call("bandmean") as Image; - } - - /// - /// Unfold image bands into x axis. - /// - /// - /// - /// using Image @out = in.Bandunfold(factor: int); - /// - /// - /// Unfold by this factor. - /// A new . - public Image Bandunfold(int? factor = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(factor), factor); - - return this.Call("bandunfold", options) as Image; - } - - /// - /// Make a black image. - /// - /// - /// - /// using Image @out = NetVips.Image.Black(width, height, bands: int); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Number of bands in image. - /// A new . - public static Image Black(int width, int height, int? bands = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(bands), bands); - - return Operation.Call("black", options, width, height) as Image; - } - - /// - /// Boolean operation on two images. - /// - /// - /// - /// using Image @out = left.Boolean(right, boolean); - /// - /// - /// Right-hand image argument. - /// Boolean to perform. - /// A new . - public Image Boolean(Image right, Enums.OperationBoolean boolean) - { - return this.Call("boolean", right, boolean) as Image; - } - - /// - /// Boolean operations against a constant. - /// - /// - /// - /// using Image @out = in.BooleanConst(boolean, c); - /// - /// - /// Boolean to perform. - /// Array of constants. - /// A new . - public Image BooleanConst(Enums.OperationBoolean boolean, double[] c) - { - return this.Call("boolean_const", boolean, c) as Image; - } - - /// - /// Build a look-up table. - /// - /// - /// - /// using Image @out = in.Buildlut(); - /// - /// - /// A new . - public Image Buildlut() - { - return this.Call("buildlut") as Image; - } - - /// - /// Byteswap an image. - /// - /// - /// - /// using Image @out = in.Byteswap(); - /// - /// - /// A new . - public Image Byteswap() - { - return this.Call("byteswap") as Image; - } - - /// - /// Cache an image. - /// - /// - /// - /// using Image @out = in.Cache(maxTiles: int, tileHeight: int, tileWidth: int); - /// - /// - /// Maximum number of tiles to cache. - /// Tile height in pixels. - /// Tile width in pixels. - /// A new . - public Image Cache(int? maxTiles = null, int? tileHeight = null, int? tileWidth = null) - { - var options = new VOption(); - - options.AddIfPresent("max_tiles", maxTiles); - options.AddIfPresent("tile_height", tileHeight); - options.AddIfPresent("tile_width", tileWidth); - - return this.Call("cache", options) as Image; - } - - /// - /// Canny edge detector. - /// - /// - /// - /// using Image @out = in.Canny(sigma: double, precision: Enums.Precision); - /// - /// - /// Sigma of Gaussian. - /// Convolve with this precision. - /// A new . - public Image Canny(double? sigma = null, Enums.Precision? precision = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(sigma), sigma); - options.AddIfPresent(nameof(precision), precision); - - return this.Call("canny", options) as Image; - } - - /// - /// Cast an image. - /// - /// - /// - /// using Image @out = in.Cast(format, shift: bool); - /// - /// - /// Format to cast to. - /// Shift integer values up and down. - /// A new . - public Image Cast(Enums.BandFormat format, bool? shift = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(shift), shift); - - return this.Call("cast", options, format) as Image; - } - - /// - /// Transform LCh to CMC. - /// - /// - /// - /// using Image @out = in.CMC2LCh(); - /// - /// - /// A new . - public Image CMC2LCh() - { - return this.Call("CMC2LCh") as Image; - } - - /// - /// Transform CMYK to XYZ. - /// - /// - /// - /// using Image @out = in.CMYK2XYZ(); - /// - /// - /// A new . - public Image CMYK2XYZ() - { - return this.Call("CMYK2XYZ") as Image; - } - - /// - /// Convert to a new colorspace. - /// - /// - /// - /// using Image @out = in.Colourspace(space, sourceSpace: Enums.Interpretation); - /// - /// - /// Destination color space. - /// Source color space. - /// A new . - public Image Colourspace(Enums.Interpretation space, Enums.Interpretation? sourceSpace = null) - { - var options = new VOption(); - - options.AddIfPresent("source_space", sourceSpace); - - return this.Call("colourspace", options, space) as Image; - } - - /// - /// Convolve with rotating mask. - /// - /// - /// - /// using Image @out = in.Compass(mask, times: int, angle: Enums.Angle45, combine: Enums.Combine, precision: Enums.Precision, layers: int, cluster: int); - /// - /// - /// Input matrix image. - /// Rotate and convolve this many times. - /// Rotate mask by this much between convolutions. - /// Combine convolution results like this. - /// Convolve with this precision. - /// Use this many layers in approximation. - /// Cluster lines closer than this in approximation. - /// A new . - public Image Compass(Image mask, int? times = null, Enums.Angle45? angle = null, Enums.Combine? combine = null, Enums.Precision? precision = null, int? layers = null, int? cluster = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(times), times); - options.AddIfPresent(nameof(angle), angle); - options.AddIfPresent(nameof(combine), combine); - options.AddIfPresent(nameof(precision), precision); - options.AddIfPresent(nameof(layers), layers); - options.AddIfPresent(nameof(cluster), cluster); - - return this.Call("compass", options, mask) as Image; - } - - /// - /// Perform a complex operation on an image. - /// - /// - /// - /// using Image @out = in.Complex(cmplx); - /// - /// - /// Complex to perform. - /// A new . - public Image Complex(Enums.OperationComplex cmplx) - { - return this.Call("complex", cmplx) as Image; - } - - /// - /// Complex binary operations on two images. - /// - /// - /// - /// using Image @out = left.Complex2(right, cmplx); - /// - /// - /// Right-hand image argument. - /// Binary complex operation to perform. - /// A new . - public Image Complex2(Image right, Enums.OperationComplex2 cmplx) - { - return this.Call("complex2", right, cmplx) as Image; - } - - /// - /// Form a complex image from two real images. - /// - /// - /// - /// using Image @out = left.Complexform(right); - /// - /// - /// Right-hand image argument. - /// A new . - public Image Complexform(Image right) - { - return this.Call("complexform", right) as Image; - } - - /// - /// Get a component from a complex image. - /// - /// - /// - /// using Image @out = in.Complexget(get); - /// - /// - /// Complex to perform. - /// A new . - public Image Complexget(Enums.OperationComplexget get) - { - return this.Call("complexget", get) as Image; - } - - /// - /// Blend a pair of images with a blend mode. - /// - /// - /// - /// using Image @out = base.Composite2(overlay, mode, x: int, y: int, compositingSpace: Enums.Interpretation, premultiplied: bool); - /// - /// - /// Overlay image. - /// VipsBlendMode to join with. - /// x position of overlay. - /// y position of overlay. - /// Composite images in this colour space. - /// Images have premultiplied alpha. - /// A new . - public Image Composite2(Image overlay, Enums.BlendMode mode, int? x = null, int? y = null, Enums.Interpretation? compositingSpace = null, bool? premultiplied = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(x), x); - options.AddIfPresent(nameof(y), y); - options.AddIfPresent("compositing_space", compositingSpace); - options.AddIfPresent(nameof(premultiplied), premultiplied); - - return this.Call("composite2", options, overlay, mode) as Image; - } - - /// - /// Convolution operation. - /// - /// - /// - /// using Image @out = in.Conv(mask, precision: Enums.Precision, layers: int, cluster: int); - /// - /// - /// Input matrix image. - /// Convolve with this precision. - /// Use this many layers in approximation. - /// Cluster lines closer than this in approximation. - /// A new . - public Image Conv(Image mask, Enums.Precision? precision = null, int? layers = null, int? cluster = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(precision), precision); - options.AddIfPresent(nameof(layers), layers); - options.AddIfPresent(nameof(cluster), cluster); - - return this.Call("conv", options, mask) as Image; - } - - /// - /// Approximate integer convolution. - /// - /// - /// - /// using Image @out = in.Conva(mask, layers: int, cluster: int); - /// - /// - /// Input matrix image. - /// Use this many layers in approximation. - /// Cluster lines closer than this in approximation. - /// A new . - public Image Conva(Image mask, int? layers = null, int? cluster = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(layers), layers); - options.AddIfPresent(nameof(cluster), cluster); - - return this.Call("conva", options, mask) as Image; - } - - /// - /// Approximate separable integer convolution. - /// - /// - /// - /// using Image @out = in.Convasep(mask, layers: int); - /// - /// - /// Input matrix image. - /// Use this many layers in approximation. - /// A new . - public Image Convasep(Image mask, int? layers = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(layers), layers); - - return this.Call("convasep", options, mask) as Image; - } - - /// - /// Float convolution operation. - /// - /// - /// - /// using Image @out = in.Convf(mask); - /// - /// - /// Input matrix image. - /// A new . - public Image Convf(Image mask) - { - return this.Call("convf", mask) as Image; - } - - /// - /// Int convolution operation. - /// - /// - /// - /// using Image @out = in.Convi(mask); - /// - /// - /// Input matrix image. - /// A new . - public Image Convi(Image mask) - { - return this.Call("convi", mask) as Image; - } - - /// - /// Separable convolution operation. - /// - /// - /// - /// using Image @out = in.Convsep(mask, precision: Enums.Precision, layers: int, cluster: int); - /// - /// - /// Input matrix image. - /// Convolve with this precision. - /// Use this many layers in approximation. - /// Cluster lines closer than this in approximation. - /// A new . - public Image Convsep(Image mask, Enums.Precision? precision = null, int? layers = null, int? cluster = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(precision), precision); - options.AddIfPresent(nameof(layers), layers); - options.AddIfPresent(nameof(cluster), cluster); - - return this.Call("convsep", options, mask) as Image; - } - - /// - /// Copy an image. - /// - /// - /// - /// using Image @out = in.Copy(width: int, height: int, bands: int, format: Enums.BandFormat, coding: Enums.Coding, interpretation: Enums.Interpretation, xres: double, yres: double, xoffset: int, yoffset: int); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Number of bands in image. - /// Pixel format in image. - /// Pixel coding. - /// Pixel interpretation. - /// Horizontal resolution in pixels/mm. - /// Vertical resolution in pixels/mm. - /// Horizontal offset of origin. - /// Vertical offset of origin. - /// A new . - public Image Copy(int? width = null, int? height = null, int? bands = null, Enums.BandFormat? format = null, Enums.Coding? coding = null, Enums.Interpretation? interpretation = null, double? xres = null, double? yres = null, int? xoffset = null, int? yoffset = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(width), width); - options.AddIfPresent(nameof(height), height); - options.AddIfPresent(nameof(bands), bands); - options.AddIfPresent(nameof(format), format); - options.AddIfPresent(nameof(coding), coding); - options.AddIfPresent(nameof(interpretation), interpretation); - options.AddIfPresent(nameof(xres), xres); - options.AddIfPresent(nameof(yres), yres); - options.AddIfPresent(nameof(xoffset), xoffset); - options.AddIfPresent(nameof(yoffset), yoffset); - - return this.Call("copy", options) as Image; - } - - /// - /// Count lines in an image. - /// - /// - /// - /// double nolines = in.Countlines(direction); - /// - /// - /// Countlines left-right or up-down. - /// A double. - public double Countlines(Enums.Direction direction) - { - return this.Call("countlines", direction) is double result ? result : 0d; - } - - /// - /// Load csv. - /// - /// - /// - /// using Image @out = NetVips.Image.Csvload(filename, skip: int, lines: int, whitespace: string, separator: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Skip this many lines at the start of the file. - /// Read this many lines from the file. - /// Set of whitespace characters. - /// Set of separator characters. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Csvload(string filename, int? skip = null, int? lines = null, string whitespace = null, string separator = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(skip), skip); - options.AddIfPresent(nameof(lines), lines); - options.AddIfPresent(nameof(whitespace), whitespace); - options.AddIfPresent(nameof(separator), separator); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("csvload", options, filename) as Image; - } - - /// - /// Load csv. - /// - /// - /// - /// using Image @out = NetVips.Image.Csvload(filename, out var flags, skip: int, lines: int, whitespace: string, separator: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// Skip this many lines at the start of the file. - /// Read this many lines from the file. - /// Set of whitespace characters. - /// Set of separator characters. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Csvload(string filename, out Enums.ForeignFlags flags, int? skip = null, int? lines = null, string whitespace = null, string separator = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(skip), skip); - options.AddIfPresent(nameof(lines), lines); - options.AddIfPresent(nameof(whitespace), whitespace); - options.AddIfPresent(nameof(separator), separator); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("csvload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load csv. - /// - /// - /// - /// using Image @out = NetVips.Image.CsvloadSource(source, skip: int, lines: int, whitespace: string, separator: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Skip this many lines at the start of the file. - /// Read this many lines from the file. - /// Set of whitespace characters. - /// Set of separator characters. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image CsvloadSource(Source source, int? skip = null, int? lines = null, string whitespace = null, string separator = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(skip), skip); - options.AddIfPresent(nameof(lines), lines); - options.AddIfPresent(nameof(whitespace), whitespace); - options.AddIfPresent(nameof(separator), separator); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("csvload_source", options, source) as Image; - } - - /// - /// Load csv. - /// - /// - /// - /// using Image @out = NetVips.Image.CsvloadStream(stream, skip: int, lines: int, whitespace: string, separator: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Skip this many lines at the start of the file. - /// Read this many lines from the file. - /// Set of whitespace characters. - /// Set of separator characters. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image CsvloadStream(Stream stream, int? skip = null, int? lines = null, string whitespace = null, string separator = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = CsvloadSource(source, skip, lines, whitespace, separator, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load csv. - /// - /// - /// - /// using Image @out = NetVips.Image.CsvloadSource(source, out var flags, skip: int, lines: int, whitespace: string, separator: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Flags for this file. - /// Skip this many lines at the start of the file. - /// Read this many lines from the file. - /// Set of whitespace characters. - /// Set of separator characters. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image CsvloadSource(Source source, out Enums.ForeignFlags flags, int? skip = null, int? lines = null, string whitespace = null, string separator = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(skip), skip); - options.AddIfPresent(nameof(lines), lines); - options.AddIfPresent(nameof(whitespace), whitespace); - options.AddIfPresent(nameof(separator), separator); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("csvload_source", options, source) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load csv. - /// - /// - /// - /// using Image @out = NetVips.Image.CsvloadStream(stream, out var flags, skip: int, lines: int, whitespace: string, separator: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Flags for this file. - /// Skip this many lines at the start of the file. - /// Read this many lines from the file. - /// Set of whitespace characters. - /// Set of separator characters. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image CsvloadStream(Stream stream, out Enums.ForeignFlags flags, int? skip = null, int? lines = null, string whitespace = null, string separator = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = CsvloadSource(source, out flags, skip, lines, whitespace, separator, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Save image to csv. - /// - /// - /// - /// in.Csvsave(filename, separator: string, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Filename to save to. - /// Separator characters. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Csvsave(string filename, string separator = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(separator), separator); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("csvsave", options, filename); - } - - /// - /// Save image to csv. - /// - /// - /// - /// in.CsvsaveTarget(target, separator: string, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Target to save to. - /// Separator characters. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void CsvsaveTarget(Target target, string separator = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(separator), separator); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("csvsave_target", options, target); - } - - /// - /// Save image to csv. - /// - /// - /// - /// in.CsvsaveStream(stream, separator: string, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Stream to save to. - /// Separator characters. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void CsvsaveStream(Stream stream, string separator = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - using var target = TargetStream.NewFromStream(stream); - CsvsaveTarget(target, separator, keep, background, pageHeight, profile); - } - - /// - /// Calculate dE00. - /// - /// - /// - /// using Image @out = left.DE00(right); - /// - /// - /// Right-hand input image. - /// A new . - public Image DE00(Image right) - { - return this.Call("dE00", right) as Image; - } - - /// - /// Calculate dE76. - /// - /// - /// - /// using Image @out = left.DE76(right); - /// - /// - /// Right-hand input image. - /// A new . - public Image DE76(Image right) - { - return this.Call("dE76", right) as Image; - } - - /// - /// Calculate dECMC. - /// - /// - /// - /// using Image @out = left.DECMC(right); - /// - /// - /// Right-hand input image. - /// A new . - public Image DECMC(Image right) - { - return this.Call("dECMC", right) as Image; - } - - /// - /// Find image standard deviation. - /// - /// - /// - /// double @out = in.Deviate(); - /// - /// - /// A double. - public double Deviate() - { - return this.Call("deviate") is double result ? result : 0d; - } - - /// - /// Divide two images. - /// - /// - /// - /// using Image @out = left.Divide(right); - /// - /// - /// Right-hand image argument. - /// A new . - public Image Divide(Image right) - { - return this.Call("divide", right) as Image; - } - - /// - /// Save image to deepzoom file. - /// - /// - /// - /// in.Dzsave(filename, imagename: string, layout: Enums.ForeignDzLayout, suffix: string, overlap: int, tileSize: int, centre: bool, depth: Enums.ForeignDzDepth, angle: Enums.Angle, container: Enums.ForeignDzContainer, compression: int, regionShrink: Enums.RegionShrink, skipBlanks: int, id: string, q: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Filename to save to. - /// Image name. - /// Directory layout. - /// Filename suffix for tiles. - /// Tile overlap in pixels. - /// Tile size in pixels. - /// Center image in tile. - /// Pyramid depth. - /// Rotate image during save. - /// Pyramid container type. - /// ZIP deflate compression level. - /// Method to shrink regions. - /// Skip tiles which are nearly equal to the background. - /// Resource ID. - /// Q factor. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Dzsave(string filename, string imagename = null, Enums.ForeignDzLayout? layout = null, string suffix = null, int? overlap = null, int? tileSize = null, bool? centre = null, Enums.ForeignDzDepth? depth = null, Enums.Angle? angle = null, Enums.ForeignDzContainer? container = null, int? compression = null, Enums.RegionShrink? regionShrink = null, int? skipBlanks = null, string id = null, int? q = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(NetVips.AtLeastLibvips(8, 15) ? nameof(imagename) : "basename", imagename); - options.AddIfPresent(nameof(layout), layout); - options.AddIfPresent(nameof(suffix), suffix); - options.AddIfPresent(nameof(overlap), overlap); - options.AddIfPresent("tile_size", tileSize); - options.AddIfPresent(nameof(centre), centre); - options.AddIfPresent(nameof(depth), depth); - options.AddIfPresent(nameof(angle), angle); - options.AddIfPresent(nameof(container), container); - options.AddIfPresent(nameof(compression), compression); - options.AddIfPresent("region_shrink", regionShrink); - options.AddIfPresent("skip_blanks", skipBlanks); - options.AddIfPresent(nameof(id), id); - options.AddIfPresent("Q", q); - options.AddForeignKeep(keep, true); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("dzsave", options, filename); - } - - /// - /// Save image to dz buffer. - /// - /// - /// - /// byte[] buffer = in.DzsaveBuffer(imagename: string, layout: Enums.ForeignDzLayout, suffix: string, overlap: int, tileSize: int, centre: bool, depth: Enums.ForeignDzDepth, angle: Enums.Angle, container: Enums.ForeignDzContainer, compression: int, regionShrink: Enums.RegionShrink, skipBlanks: int, id: string, q: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Image name. - /// Directory layout. - /// Filename suffix for tiles. - /// Tile overlap in pixels. - /// Tile size in pixels. - /// Center image in tile. - /// Pyramid depth. - /// Rotate image during save. - /// Pyramid container type. - /// ZIP deflate compression level. - /// Method to shrink regions. - /// Skip tiles which are nearly equal to the background. - /// Resource ID. - /// Q factor. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - /// An array of bytes. - public byte[] DzsaveBuffer(string imagename = null, Enums.ForeignDzLayout? layout = null, string suffix = null, int? overlap = null, int? tileSize = null, bool? centre = null, Enums.ForeignDzDepth? depth = null, Enums.Angle? angle = null, Enums.ForeignDzContainer? container = null, int? compression = null, Enums.RegionShrink? regionShrink = null, int? skipBlanks = null, string id = null, int? q = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(NetVips.AtLeastLibvips(8, 15) ? nameof(imagename) : "basename", imagename); - options.AddIfPresent(nameof(layout), layout); - options.AddIfPresent(nameof(suffix), suffix); - options.AddIfPresent(nameof(overlap), overlap); - options.AddIfPresent("tile_size", tileSize); - options.AddIfPresent(nameof(centre), centre); - options.AddIfPresent(nameof(depth), depth); - options.AddIfPresent(nameof(angle), angle); - options.AddIfPresent(nameof(container), container); - options.AddIfPresent(nameof(compression), compression); - options.AddIfPresent("region_shrink", regionShrink); - options.AddIfPresent("skip_blanks", skipBlanks); - options.AddIfPresent(nameof(id), id); - options.AddIfPresent("Q", q); - options.AddForeignKeep(keep, true); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - return this.Call("dzsave_buffer", options) as byte[]; - } - - /// - /// Save image to deepzoom target. - /// - /// - /// - /// in.DzsaveTarget(target, imagename: string, layout: Enums.ForeignDzLayout, suffix: string, overlap: int, tileSize: int, centre: bool, depth: Enums.ForeignDzDepth, angle: Enums.Angle, container: Enums.ForeignDzContainer, compression: int, regionShrink: Enums.RegionShrink, skipBlanks: int, id: string, q: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Target to save to. - /// Image name. - /// Directory layout. - /// Filename suffix for tiles. - /// Tile overlap in pixels. - /// Tile size in pixels. - /// Center image in tile. - /// Pyramid depth. - /// Rotate image during save. - /// Pyramid container type. - /// ZIP deflate compression level. - /// Method to shrink regions. - /// Skip tiles which are nearly equal to the background. - /// Resource ID. - /// Q factor. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void DzsaveTarget(Target target, string imagename = null, Enums.ForeignDzLayout? layout = null, string suffix = null, int? overlap = null, int? tileSize = null, bool? centre = null, Enums.ForeignDzDepth? depth = null, Enums.Angle? angle = null, Enums.ForeignDzContainer? container = null, int? compression = null, Enums.RegionShrink? regionShrink = null, int? skipBlanks = null, string id = null, int? q = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(NetVips.AtLeastLibvips(8, 15) ? nameof(imagename) : "basename", imagename); - options.AddIfPresent(nameof(layout), layout); - options.AddIfPresent(nameof(suffix), suffix); - options.AddIfPresent(nameof(overlap), overlap); - options.AddIfPresent("tile_size", tileSize); - options.AddIfPresent(nameof(centre), centre); - options.AddIfPresent(nameof(depth), depth); - options.AddIfPresent(nameof(angle), angle); - options.AddIfPresent(nameof(container), container); - options.AddIfPresent(nameof(compression), compression); - options.AddIfPresent("region_shrink", regionShrink); - options.AddIfPresent("skip_blanks", skipBlanks); - options.AddIfPresent(nameof(id), id); - options.AddIfPresent("Q", q); - options.AddForeignKeep(keep, true); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("dzsave_target", options, target); - } - - /// - /// Save image to deepzoom stream. - /// - /// - /// - /// in.DzsaveStream(stream, imagename: string, layout: Enums.ForeignDzLayout, suffix: string, overlap: int, tileSize: int, centre: bool, depth: Enums.ForeignDzDepth, angle: Enums.Angle, container: Enums.ForeignDzContainer, compression: int, regionShrink: Enums.RegionShrink, skipBlanks: int, id: string, q: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Stream to save to. - /// Image name. - /// Directory layout. - /// Filename suffix for tiles. - /// Tile overlap in pixels. - /// Tile size in pixels. - /// Center image in tile. - /// Pyramid depth. - /// Rotate image during save. - /// Pyramid container type. - /// ZIP deflate compression level. - /// Method to shrink regions. - /// Skip tiles which are nearly equal to the background. - /// Resource ID. - /// Q factor. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void DzsaveStream(Stream stream, string imagename = null, Enums.ForeignDzLayout? layout = null, string suffix = null, int? overlap = null, int? tileSize = null, bool? centre = null, Enums.ForeignDzDepth? depth = null, Enums.Angle? angle = null, Enums.ForeignDzContainer? container = null, int? compression = null, Enums.RegionShrink? regionShrink = null, int? skipBlanks = null, string id = null, int? q = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - using var target = TargetStream.NewFromStream(stream); - DzsaveTarget(target, imagename, layout, suffix, overlap, tileSize, centre, depth, angle, container, compression, regionShrink, skipBlanks, id, q, keep, background, pageHeight, profile); - } - - /// - /// Embed an image in a larger image. - /// - /// - /// - /// using Image @out = in.Embed(x, y, width, height, extend: Enums.Extend, background: double[]); - /// - /// - /// Left edge of input in output. - /// Top edge of input in output. - /// Image width in pixels. - /// Image height in pixels. - /// How to generate the extra pixels. - /// Color for background pixels. - /// A new . - public Image Embed(int x, int y, int width, int height, Enums.Extend? extend = null, double[] background = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(extend), extend); - options.AddIfPresent(nameof(background), background); - - return this.Call("embed", options, x, y, width, height) as Image; - } - - /// - /// Extract an area from an image. - /// - /// - /// - /// using Image @out = input.ExtractArea(left, top, width, height); - /// - /// - /// Left edge of extract area. - /// Top edge of extract area. - /// Width of extract area. - /// Height of extract area. - /// A new . - public Image ExtractArea(int left, int top, int width, int height) - { - return this.Call("extract_area", left, top, width, height) as Image; - } - - /// - /// Extract band from an image. - /// - /// - /// - /// using Image @out = in.ExtractBand(band, n: int); - /// - /// - /// Band to extract. - /// Number of bands to extract. - /// A new . - public Image ExtractBand(int band, int? n = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(n), n); - - return this.Call("extract_band", options, band) as Image; - } - - /// - /// Make an image showing the eye's spatial response. - /// - /// - /// - /// using Image @out = NetVips.Image.Eye(width, height, uchar: bool, factor: double); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Output an unsigned char image. - /// Maximum spatial frequency. - /// A new . - public static Image Eye(int width, int height, bool? uchar = null, double? factor = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(uchar), uchar); - options.AddIfPresent(nameof(factor), factor); - - return Operation.Call("eye", options, width, height) as Image; - } - - /// - /// False-color an image. - /// - /// - /// - /// using Image @out = in.Falsecolour(); - /// - /// - /// A new . - public Image Falsecolour() - { - return this.Call("falsecolour") as Image; - } - - /// - /// Fast correlation. - /// - /// - /// - /// using Image @out = in.Fastcor(@ref); - /// - /// - /// Input reference image. - /// A new . - public Image Fastcor(Image @ref) - { - return this.Call("fastcor", @ref) as Image; - } - - /// - /// Fill image zeros with nearest non-zero pixel. - /// - /// - /// - /// using Image @out = in.FillNearest(); - /// - /// - /// A new . - public Image FillNearest() - { - return this.Call("fill_nearest") as Image; - } - - /// - /// Fill image zeros with nearest non-zero pixel. - /// - /// - /// - /// using Image @out = in.FillNearest(out var distance); - /// - /// - /// Distance to nearest non-zero pixel. - /// A new . - public Image FillNearest(out Image distance) - { - var optionalOutput = new VOption - { - {"distance", true} - }; - - var results = this.Call("fill_nearest", optionalOutput) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - distance = opts?["distance"] as Image; - - return finalResult; - } - - /// - /// Search an image for non-edge areas. - /// - /// - /// - /// var output = in.FindTrim(threshold: double, background: double[], lineArt: bool); - /// - /// - /// Object threshold. - /// Color for background pixels. - /// Enable line art mode. - /// An array of objects. - public object[] FindTrim(double? threshold = null, double[] background = null, bool? lineArt = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(threshold), threshold); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("line_art", lineArt); - - return this.Call("find_trim", options) as object[]; - } - - /// - /// Load a FITS image. - /// - /// - /// - /// using Image @out = NetVips.Image.Fitsload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Fitsload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("fitsload", options, filename) as Image; - } - - /// - /// Load a FITS image. - /// - /// - /// - /// using Image @out = NetVips.Image.Fitsload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Fitsload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("fitsload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load FITS from a source. - /// - /// - /// - /// using Image @out = NetVips.Image.FitsloadSource(source, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image FitsloadSource(Source source, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("fitsload_source", options, source) as Image; - } - - /// - /// Load FITS from a stream. - /// - /// - /// - /// using Image @out = NetVips.Image.FitsloadStream(stream, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image FitsloadStream(Stream stream, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = FitsloadSource(source, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load FITS from a source. - /// - /// - /// - /// using Image @out = NetVips.Image.FitsloadSource(source, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image FitsloadSource(Source source, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("fitsload_source", options, source) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load FITS from a stream. - /// - /// - /// - /// using Image @out = NetVips.Image.FitsloadStream(stream, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image FitsloadStream(Stream stream, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = FitsloadSource(source, out flags, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Save image to fits file. - /// - /// - /// - /// in.Fitssave(filename, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Filename to save to. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Fitssave(string filename, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("fitssave", options, filename); - } - - /// - /// Flatten alpha out of an image. - /// - /// - /// - /// using Image @out = in.Flatten(background: double[], maxAlpha: double); - /// - /// - /// Background value. - /// Maximum value of alpha channel. - /// A new . - public Image Flatten(double[] background = null, double? maxAlpha = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("max_alpha", maxAlpha); - - return this.Call("flatten", options) as Image; - } - - /// - /// Flip an image. - /// - /// - /// - /// using Image @out = in.Flip(direction); - /// - /// - /// Direction to flip image. - /// A new . - public Image Flip(Enums.Direction direction) - { - return this.Call("flip", direction) as Image; - } - - /// - /// Transform float RGB to Radiance coding. - /// - /// - /// - /// using Image @out = in.Float2rad(); - /// - /// - /// A new . - public Image Float2rad() - { - return this.Call("float2rad") as Image; - } - - /// - /// Make a fractal surface. - /// - /// - /// - /// using Image @out = NetVips.Image.Fractsurf(width, height, fractalDimension); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Fractal dimension. - /// A new . - public static Image Fractsurf(int width, int height, double fractalDimension) - { - return Operation.Call("fractsurf", width, height, fractalDimension) as Image; - } - - /// - /// Frequency-domain filtering. - /// - /// - /// - /// using Image @out = in.Freqmult(mask); - /// - /// - /// Input mask image. - /// A new . - public Image Freqmult(Image mask) - { - return this.Call("freqmult", mask) as Image; - } - - /// - /// Forward FFT. - /// - /// - /// - /// using Image @out = in.Fwfft(); - /// - /// - /// A new . - public Image Fwfft() - { - return this.Call("fwfft") as Image; - } - - /// - /// Gamma an image. - /// - /// - /// - /// using Image @out = in.Gamma(exponent: double); - /// - /// - /// Gamma factor. - /// A new . - public Image Gamma(double? exponent = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(exponent), exponent); - - return this.Call("gamma", options) as Image; - } - - /// - /// Gaussian blur. - /// - /// - /// - /// using Image @out = in.Gaussblur(sigma, minAmpl: double, precision: Enums.Precision); - /// - /// - /// Sigma of Gaussian. - /// Minimum amplitude of Gaussian. - /// Convolve with this precision. - /// A new . - public Image Gaussblur(double sigma, double? minAmpl = null, Enums.Precision? precision = null) - { - var options = new VOption(); - - options.AddIfPresent("min_ampl", minAmpl); - options.AddIfPresent(nameof(precision), precision); - - return this.Call("gaussblur", options, sigma) as Image; - } - - /// - /// Make a gaussian image. - /// - /// - /// - /// using Image @out = NetVips.Image.Gaussmat(sigma, minAmpl, separable: bool, precision: Enums.Precision); - /// - /// - /// Sigma of Gaussian. - /// Minimum amplitude of Gaussian. - /// Generate separable Gaussian. - /// Generate with this precision. - /// A new . - public static Image Gaussmat(double sigma, double minAmpl, bool? separable = null, Enums.Precision? precision = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(separable), separable); - options.AddIfPresent(nameof(precision), precision); - - return Operation.Call("gaussmat", options, sigma, minAmpl) as Image; - } - - /// - /// Make a gaussnoise image. - /// - /// - /// - /// using Image @out = NetVips.Image.Gaussnoise(width, height, sigma: double, mean: double, seed: int); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Standard deviation of pixels in generated image. - /// Mean of pixels in generated image. - /// Random number seed. - /// A new . - public static Image Gaussnoise(int width, int height, double? sigma = null, double? mean = null, int? seed = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(sigma), sigma); - options.AddIfPresent(nameof(mean), mean); - options.AddIfPresent(nameof(seed), seed); - - return Operation.Call("gaussnoise", options, width, height) as Image; - } - - /// - /// Read a point from an image. - /// - /// - /// - /// double[] outArray = in.Getpoint(x, y); - /// - /// - /// Point to read. - /// Point to read. - /// An array of doubles. - public double[] Getpoint(int x, int y) - { - return this.Call("getpoint", x, y) as double[]; - } - - /// - /// Load GIF with libnsgif. - /// - /// - /// - /// using Image @out = NetVips.Image.Gifload(filename, n: int, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Number of pages to load, -1 for all. - /// First page to load. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Gifload(string filename, int? n = null, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("gifload", options, filename) as Image; - } - - /// - /// Load GIF with libnsgif. - /// - /// - /// - /// using Image @out = NetVips.Image.Gifload(filename, out var flags, n: int, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// Number of pages to load, -1 for all. - /// First page to load. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Gifload(string filename, out Enums.ForeignFlags flags, int? n = null, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("gifload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load GIF with libnsgif. - /// - /// - /// - /// using Image @out = NetVips.Image.GifloadBuffer(buffer, n: int, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Number of pages to load, -1 for all. - /// First page to load. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image GifloadBuffer(byte[] buffer, int? n = null, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("gifload_buffer", options, buffer) as Image; - } - - /// - /// Load GIF with libnsgif. - /// - /// - /// - /// using Image @out = NetVips.Image.GifloadBuffer(buffer, out var flags, n: int, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Flags for this file. - /// Number of pages to load, -1 for all. - /// First page to load. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image GifloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, int? n = null, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("gifload_buffer", options, buffer) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load gif from source. - /// - /// - /// - /// using Image @out = NetVips.Image.GifloadSource(source, n: int, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Number of pages to load, -1 for all. - /// First page to load. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image GifloadSource(Source source, int? n = null, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("gifload_source", options, source) as Image; - } - - /// - /// Load gif from stream. - /// - /// - /// - /// using Image @out = NetVips.Image.GifloadStream(stream, n: int, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Number of pages to load, -1 for all. - /// First page to load. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image GifloadStream(Stream stream, int? n = null, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = GifloadSource(source, n, page, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load gif from source. - /// - /// - /// - /// using Image @out = NetVips.Image.GifloadSource(source, out var flags, n: int, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Flags for this file. - /// Number of pages to load, -1 for all. - /// First page to load. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image GifloadSource(Source source, out Enums.ForeignFlags flags, int? n = null, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("gifload_source", options, source) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load gif from stream. - /// - /// - /// - /// using Image @out = NetVips.Image.GifloadStream(stream, out var flags, n: int, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Flags for this file. - /// Number of pages to load, -1 for all. - /// First page to load. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image GifloadStream(Stream stream, out Enums.ForeignFlags flags, int? n = null, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = GifloadSource(source, out flags, n, page, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Save as gif. - /// - /// - /// - /// in.Gifsave(filename, dither: double, effort: int, bitdepth: int, interframeMaxerror: double, reuse: bool, interpaletteMaxerror: double, interlace: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Filename to save to. - /// Amount of dithering. - /// Quantisation effort. - /// Number of bits per pixel. - /// Maximum inter-frame error for transparency. - /// Reuse palette from input. - /// Maximum inter-palette error for palette reusage. - /// Generate an interlaced (progressive) GIF. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Gifsave(string filename, double? dither = null, int? effort = null, int? bitdepth = null, double? interframeMaxerror = null, bool? reuse = null, double? interpaletteMaxerror = null, bool? interlace = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(dither), dither); - options.AddIfPresent(nameof(effort), effort); - options.AddIfPresent(nameof(bitdepth), bitdepth); - options.AddIfPresent("interframe_maxerror", interframeMaxerror); - options.AddIfPresent(nameof(reuse), reuse); - options.AddIfPresent("interpalette_maxerror", interpaletteMaxerror); - options.AddIfPresent(nameof(interlace), interlace); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("gifsave", options, filename); - } - - /// - /// Save as gif. - /// - /// - /// - /// byte[] buffer = in.GifsaveBuffer(dither: double, effort: int, bitdepth: int, interframeMaxerror: double, reuse: bool, interpaletteMaxerror: double, interlace: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Amount of dithering. - /// Quantisation effort. - /// Number of bits per pixel. - /// Maximum inter-frame error for transparency. - /// Reuse palette from input. - /// Maximum inter-palette error for palette reusage. - /// Generate an interlaced (progressive) GIF. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - /// An array of bytes. - public byte[] GifsaveBuffer(double? dither = null, int? effort = null, int? bitdepth = null, double? interframeMaxerror = null, bool? reuse = null, double? interpaletteMaxerror = null, bool? interlace = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(dither), dither); - options.AddIfPresent(nameof(effort), effort); - options.AddIfPresent(nameof(bitdepth), bitdepth); - options.AddIfPresent("interframe_maxerror", interframeMaxerror); - options.AddIfPresent(nameof(reuse), reuse); - options.AddIfPresent("interpalette_maxerror", interpaletteMaxerror); - options.AddIfPresent(nameof(interlace), interlace); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - return this.Call("gifsave_buffer", options) as byte[]; - } - - /// - /// Save as gif. - /// - /// - /// - /// in.GifsaveTarget(target, dither: double, effort: int, bitdepth: int, interframeMaxerror: double, reuse: bool, interpaletteMaxerror: double, interlace: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Target to save to. - /// Amount of dithering. - /// Quantisation effort. - /// Number of bits per pixel. - /// Maximum inter-frame error for transparency. - /// Reuse palette from input. - /// Maximum inter-palette error for palette reusage. - /// Generate an interlaced (progressive) GIF. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void GifsaveTarget(Target target, double? dither = null, int? effort = null, int? bitdepth = null, double? interframeMaxerror = null, bool? reuse = null, double? interpaletteMaxerror = null, bool? interlace = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(dither), dither); - options.AddIfPresent(nameof(effort), effort); - options.AddIfPresent(nameof(bitdepth), bitdepth); - options.AddIfPresent("interframe_maxerror", interframeMaxerror); - options.AddIfPresent(nameof(reuse), reuse); - options.AddIfPresent("interpalette_maxerror", interpaletteMaxerror); - options.AddIfPresent(nameof(interlace), interlace); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("gifsave_target", options, target); - } - - /// - /// Save as gif. - /// - /// - /// - /// in.GifsaveStream(stream, dither: double, effort: int, bitdepth: int, interframeMaxerror: double, reuse: bool, interpaletteMaxerror: double, interlace: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Stream to save to. - /// Amount of dithering. - /// Quantisation effort. - /// Number of bits per pixel. - /// Maximum inter-frame error for transparency. - /// Reuse palette from input. - /// Maximum inter-palette error for palette reusage. - /// Generate an interlaced (progressive) GIF. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void GifsaveStream(Stream stream, double? dither = null, int? effort = null, int? bitdepth = null, double? interframeMaxerror = null, bool? reuse = null, double? interpaletteMaxerror = null, bool? interlace = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - using var target = TargetStream.NewFromStream(stream); - GifsaveTarget(target, dither, effort, bitdepth, interframeMaxerror, reuse, interpaletteMaxerror, interlace, keep, background, pageHeight, profile); - } - - /// - /// Global balance an image mosaic. - /// - /// - /// - /// using Image @out = in.Globalbalance(gamma: double, intOutput: bool); - /// - /// - /// Image gamma. - /// Integer output. - /// A new . - public Image Globalbalance(double? gamma = null, bool? intOutput = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(gamma), gamma); - options.AddIfPresent("int_output", intOutput); - - return this.Call("globalbalance", options) as Image; - } - - /// - /// Place an image within a larger image with a certain gravity. - /// - /// - /// - /// using Image @out = in.Gravity(direction, width, height, extend: Enums.Extend, background: double[]); - /// - /// - /// Direction to place image within width/height. - /// Image width in pixels. - /// Image height in pixels. - /// How to generate the extra pixels. - /// Color for background pixels. - /// A new . - public Image Gravity(Enums.CompassDirection direction, int width, int height, Enums.Extend? extend = null, double[] background = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(extend), extend); - options.AddIfPresent(nameof(background), background); - - return this.Call("gravity", options, direction, width, height) as Image; - } - - /// - /// Make a grey ramp image. - /// - /// - /// - /// using Image @out = NetVips.Image.Grey(width, height, uchar: bool); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Output an unsigned char image. - /// A new . - public static Image Grey(int width, int height, bool? uchar = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(uchar), uchar); - - return Operation.Call("grey", options, width, height) as Image; - } - - /// - /// Grid an image. - /// - /// - /// - /// using Image @out = in.Grid(tileHeight, across, down); - /// - /// - /// Chop into tiles this high. - /// Number of tiles across. - /// Number of tiles down. - /// A new . - public Image Grid(int tileHeight, int across, int down) - { - return this.Call("grid", tileHeight, across, down) as Image; - } - - /// - /// Load a HEIF image. - /// - /// - /// - /// using Image @out = NetVips.Image.Heifload(filename, page: int, n: int, thumbnail: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Fetch thumbnail image. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Heifload(string filename, int? page = null, int? n = null, bool? thumbnail = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(thumbnail), thumbnail); - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("heifload", options, filename) as Image; - } - - /// - /// Load a HEIF image. - /// - /// - /// - /// using Image @out = NetVips.Image.Heifload(filename, out var flags, page: int, n: int, thumbnail: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Fetch thumbnail image. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Heifload(string filename, out Enums.ForeignFlags flags, int? page = null, int? n = null, bool? thumbnail = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(thumbnail), thumbnail); - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("heifload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load a HEIF image. - /// - /// - /// - /// using Image @out = NetVips.Image.HeifloadBuffer(buffer, page: int, n: int, thumbnail: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Fetch thumbnail image. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image HeifloadBuffer(byte[] buffer, int? page = null, int? n = null, bool? thumbnail = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(thumbnail), thumbnail); - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("heifload_buffer", options, buffer) as Image; - } - - /// - /// Load a HEIF image. - /// - /// - /// - /// using Image @out = NetVips.Image.HeifloadBuffer(buffer, out var flags, page: int, n: int, thumbnail: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Flags for this file. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Fetch thumbnail image. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image HeifloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, int? page = null, int? n = null, bool? thumbnail = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(thumbnail), thumbnail); - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("heifload_buffer", options, buffer) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load a HEIF image. - /// - /// - /// - /// using Image @out = NetVips.Image.HeifloadSource(source, page: int, n: int, thumbnail: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Fetch thumbnail image. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image HeifloadSource(Source source, int? page = null, int? n = null, bool? thumbnail = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(thumbnail), thumbnail); - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("heifload_source", options, source) as Image; - } - - /// - /// Load a HEIF image. - /// - /// - /// - /// using Image @out = NetVips.Image.HeifloadStream(stream, page: int, n: int, thumbnail: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Fetch thumbnail image. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image HeifloadStream(Stream stream, int? page = null, int? n = null, bool? thumbnail = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = HeifloadSource(source, page, n, thumbnail, unlimited, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load a HEIF image. - /// - /// - /// - /// using Image @out = NetVips.Image.HeifloadSource(source, out var flags, page: int, n: int, thumbnail: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Flags for this file. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Fetch thumbnail image. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image HeifloadSource(Source source, out Enums.ForeignFlags flags, int? page = null, int? n = null, bool? thumbnail = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(thumbnail), thumbnail); - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("heifload_source", options, source) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load a HEIF image. - /// - /// - /// - /// using Image @out = NetVips.Image.HeifloadStream(stream, out var flags, page: int, n: int, thumbnail: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Flags for this file. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Fetch thumbnail image. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image HeifloadStream(Stream stream, out Enums.ForeignFlags flags, int? page = null, int? n = null, bool? thumbnail = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = HeifloadSource(source, out flags, page, n, thumbnail, unlimited, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Save image in HEIF format. - /// - /// - /// - /// in.Heifsave(filename, q: int, bitdepth: int, lossless: bool, compression: Enums.ForeignHeifCompression, effort: int, subsampleMode: Enums.ForeignSubsample, encoder: Enums.ForeignHeifEncoder, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Filename to save to. - /// Q factor. - /// Number of bits per pixel. - /// Enable lossless compression. - /// Compression format. - /// CPU effort. - /// Select chroma subsample operation mode. - /// Select encoder to use. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Heifsave(string filename, int? q = null, int? bitdepth = null, bool? lossless = null, Enums.ForeignHeifCompression? compression = null, int? effort = null, Enums.ForeignSubsample? subsampleMode = null, Enums.ForeignHeifEncoder? encoder = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent("Q", q); - options.AddIfPresent(nameof(bitdepth), bitdepth); - options.AddIfPresent(nameof(lossless), lossless); - options.AddIfPresent(nameof(compression), compression); - options.AddIfPresent(nameof(effort), effort); - options.AddIfPresent("subsample_mode", subsampleMode); - options.AddIfPresent(nameof(encoder), encoder); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("heifsave", options, filename); - } - - /// - /// Save image in HEIF format. - /// - /// - /// - /// byte[] buffer = in.HeifsaveBuffer(q: int, bitdepth: int, lossless: bool, compression: Enums.ForeignHeifCompression, effort: int, subsampleMode: Enums.ForeignSubsample, encoder: Enums.ForeignHeifEncoder, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Q factor. - /// Number of bits per pixel. - /// Enable lossless compression. - /// Compression format. - /// CPU effort. - /// Select chroma subsample operation mode. - /// Select encoder to use. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - /// An array of bytes. - public byte[] HeifsaveBuffer(int? q = null, int? bitdepth = null, bool? lossless = null, Enums.ForeignHeifCompression? compression = null, int? effort = null, Enums.ForeignSubsample? subsampleMode = null, Enums.ForeignHeifEncoder? encoder = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent("Q", q); - options.AddIfPresent(nameof(bitdepth), bitdepth); - options.AddIfPresent(nameof(lossless), lossless); - options.AddIfPresent(nameof(compression), compression); - options.AddIfPresent(nameof(effort), effort); - options.AddIfPresent("subsample_mode", subsampleMode); - options.AddIfPresent(nameof(encoder), encoder); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - return this.Call("heifsave_buffer", options) as byte[]; - } - - /// - /// Save image in HEIF format. - /// - /// - /// - /// in.HeifsaveTarget(target, q: int, bitdepth: int, lossless: bool, compression: Enums.ForeignHeifCompression, effort: int, subsampleMode: Enums.ForeignSubsample, encoder: Enums.ForeignHeifEncoder, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Target to save to. - /// Q factor. - /// Number of bits per pixel. - /// Enable lossless compression. - /// Compression format. - /// CPU effort. - /// Select chroma subsample operation mode. - /// Select encoder to use. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void HeifsaveTarget(Target target, int? q = null, int? bitdepth = null, bool? lossless = null, Enums.ForeignHeifCompression? compression = null, int? effort = null, Enums.ForeignSubsample? subsampleMode = null, Enums.ForeignHeifEncoder? encoder = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent("Q", q); - options.AddIfPresent(nameof(bitdepth), bitdepth); - options.AddIfPresent(nameof(lossless), lossless); - options.AddIfPresent(nameof(compression), compression); - options.AddIfPresent(nameof(effort), effort); - options.AddIfPresent("subsample_mode", subsampleMode); - options.AddIfPresent(nameof(encoder), encoder); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("heifsave_target", options, target); - } - - /// - /// Save image in HEIF format. - /// - /// - /// - /// in.HeifsaveStream(stream, q: int, bitdepth: int, lossless: bool, compression: Enums.ForeignHeifCompression, effort: int, subsampleMode: Enums.ForeignSubsample, encoder: Enums.ForeignHeifEncoder, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Stream to save to. - /// Q factor. - /// Number of bits per pixel. - /// Enable lossless compression. - /// Compression format. - /// CPU effort. - /// Select chroma subsample operation mode. - /// Select encoder to use. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void HeifsaveStream(Stream stream, int? q = null, int? bitdepth = null, bool? lossless = null, Enums.ForeignHeifCompression? compression = null, int? effort = null, Enums.ForeignSubsample? subsampleMode = null, Enums.ForeignHeifEncoder? encoder = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - using var target = TargetStream.NewFromStream(stream); - HeifsaveTarget(target, q, bitdepth, lossless, compression, effort, subsampleMode, encoder, keep, background, pageHeight, profile); - } - - /// - /// Form cumulative histogram. - /// - /// - /// - /// using Image @out = in.HistCum(); - /// - /// - /// A new . - public Image HistCum() - { - return this.Call("hist_cum") as Image; - } - - /// - /// Estimate image entropy. - /// - /// - /// - /// double @out = in.HistEntropy(); - /// - /// - /// A double. - public double HistEntropy() - { - return this.Call("hist_entropy") is double result ? result : 0d; - } - - /// - /// Histogram equalisation. - /// - /// - /// - /// using Image @out = in.HistEqual(band: int); - /// - /// - /// Equalise with this band. - /// A new . - public Image HistEqual(int? band = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(band), band); - - return this.Call("hist_equal", options) as Image; - } - - /// - /// Find image histogram. - /// - /// - /// - /// using Image @out = in.HistFind(band: int); - /// - /// - /// Find histogram of band. - /// A new . - public Image HistFind(int? band = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(band), band); - - return this.Call("hist_find", options) as Image; - } - - /// - /// Find indexed image histogram. - /// - /// - /// - /// using Image @out = in.HistFindIndexed(index, combine: Enums.Combine); - /// - /// - /// Index image. - /// Combine bins like this. - /// A new . - public Image HistFindIndexed(Image index, Enums.Combine? combine = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(combine), combine); - - return this.Call("hist_find_indexed", options, index) as Image; - } - - /// - /// Find n-dimensional image histogram. - /// - /// - /// - /// using Image @out = in.HistFindNdim(bins: int); - /// - /// - /// Number of bins in each dimension. - /// A new . - public Image HistFindNdim(int? bins = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(bins), bins); - - return this.Call("hist_find_ndim", options) as Image; - } - - /// - /// Test for monotonicity. - /// - /// - /// - /// bool monotonic = in.HistIsmonotonic(); - /// - /// - /// A bool. - public bool HistIsmonotonic() - { - return this.Call("hist_ismonotonic") is bool result && result; - } - - /// - /// Local histogram equalisation. - /// - /// - /// - /// using Image @out = in.HistLocal(width, height, maxSlope: int); - /// - /// - /// Window width in pixels. - /// Window height in pixels. - /// Maximum slope (CLAHE). - /// A new . - public Image HistLocal(int width, int height, int? maxSlope = null) - { - var options = new VOption(); - - options.AddIfPresent("max_slope", maxSlope); - - return this.Call("hist_local", options, width, height) as Image; - } - - /// - /// Match two histograms. - /// - /// - /// - /// using Image @out = in.HistMatch(@ref); - /// - /// - /// Reference histogram. - /// A new . - public Image HistMatch(Image @ref) - { - return this.Call("hist_match", @ref) as Image; - } - - /// - /// Normalise histogram. - /// - /// - /// - /// using Image @out = in.HistNorm(); - /// - /// - /// A new . - public Image HistNorm() - { - return this.Call("hist_norm") as Image; - } - - /// - /// Plot histogram. - /// - /// - /// - /// using Image @out = in.HistPlot(); - /// - /// - /// A new . - public Image HistPlot() - { - return this.Call("hist_plot") as Image; - } - - /// - /// Find hough circle transform. - /// - /// - /// - /// using Image @out = in.HoughCircle(scale: int, minRadius: int, maxRadius: int); - /// - /// - /// Scale down dimensions by this factor. - /// Smallest radius to search for. - /// Largest radius to search for. - /// A new . - public Image HoughCircle(int? scale = null, int? minRadius = null, int? maxRadius = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent("min_radius", minRadius); - options.AddIfPresent("max_radius", maxRadius); - - return this.Call("hough_circle", options) as Image; - } - - /// - /// Find hough line transform. - /// - /// - /// - /// using Image @out = in.HoughLine(width: int, height: int); - /// - /// - /// Horizontal size of parameter space. - /// Vertical size of parameter space. - /// A new . - public Image HoughLine(int? width = null, int? height = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(width), width); - options.AddIfPresent(nameof(height), height); - - return this.Call("hough_line", options) as Image; - } - - /// - /// Transform HSV to sRGB. - /// - /// - /// - /// using Image @out = in.HSV2sRGB(); - /// - /// - /// A new . - public Image HSV2sRGB() - { - return this.Call("HSV2sRGB") as Image; - } - - /// - /// Output to device with ICC profile. - /// - /// - /// - /// using Image @out = in.IccExport(pcs: Enums.PCS, intent: Enums.Intent, blackPointCompensation: bool, outputProfile: string, depth: int); - /// - /// - /// Set Profile Connection Space. - /// Rendering intent. - /// Enable black point compensation. - /// Filename to load output profile from. - /// Output device space depth in bits. - /// A new . - public Image IccExport(Enums.PCS? pcs = null, Enums.Intent? intent = null, bool? blackPointCompensation = null, string outputProfile = null, int? depth = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(pcs), pcs); - options.AddIfPresent(nameof(intent), intent); - options.AddIfPresent("black_point_compensation", blackPointCompensation); - options.AddIfPresent("output_profile", outputProfile); - options.AddIfPresent(nameof(depth), depth); - - return this.Call("icc_export", options) as Image; - } - - /// - /// Import from device with ICC profile. - /// - /// - /// - /// using Image @out = in.IccImport(pcs: Enums.PCS, intent: Enums.Intent, blackPointCompensation: bool, embedded: bool, inputProfile: string); - /// - /// - /// Set Profile Connection Space. - /// Rendering intent. - /// Enable black point compensation. - /// Use embedded input profile, if available. - /// Filename to load input profile from. - /// A new . - public Image IccImport(Enums.PCS? pcs = null, Enums.Intent? intent = null, bool? blackPointCompensation = null, bool? embedded = null, string inputProfile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(pcs), pcs); - options.AddIfPresent(nameof(intent), intent); - options.AddIfPresent("black_point_compensation", blackPointCompensation); - options.AddIfPresent(nameof(embedded), embedded); - options.AddIfPresent("input_profile", inputProfile); - - return this.Call("icc_import", options) as Image; - } - - /// - /// Transform between devices with ICC profiles. - /// - /// - /// - /// using Image @out = in.IccTransform(outputProfile, pcs: Enums.PCS, intent: Enums.Intent, blackPointCompensation: bool, embedded: bool, inputProfile: string, depth: int); - /// - /// - /// Filename to load output profile from. - /// Set Profile Connection Space. - /// Rendering intent. - /// Enable black point compensation. - /// Use embedded input profile, if available. - /// Filename to load input profile from. - /// Output device space depth in bits. - /// A new . - public Image IccTransform(string outputProfile, Enums.PCS? pcs = null, Enums.Intent? intent = null, bool? blackPointCompensation = null, bool? embedded = null, string inputProfile = null, int? depth = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(pcs), pcs); - options.AddIfPresent(nameof(intent), intent); - options.AddIfPresent("black_point_compensation", blackPointCompensation); - options.AddIfPresent(nameof(embedded), embedded); - options.AddIfPresent("input_profile", inputProfile); - options.AddIfPresent(nameof(depth), depth); - - return this.Call("icc_transform", options, outputProfile) as Image; - } - - /// - /// Make a 1D image where pixel values are indexes. - /// - /// - /// - /// using Image @out = NetVips.Image.Identity(bands: int, @ushort: bool, size: int); - /// - /// - /// Number of bands in LUT. - /// Create a 16-bit LUT. - /// Size of 16-bit LUT. - /// A new . - public static Image Identity(int? bands = null, bool? @ushort = null, int? size = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(bands), bands); - options.AddIfPresent("ushort", @ushort); - options.AddIfPresent(nameof(size), size); - - return Operation.Call("identity", options) as Image; - } - - /// - /// Insert image @sub into @main at @x, @y. - /// - /// - /// - /// using Image @out = main.Insert(sub, x, y, expand: bool, background: double[]); - /// - /// - /// Sub-image to insert into main image. - /// Left edge of sub in main. - /// Top edge of sub in main. - /// Expand output to hold all of both inputs. - /// Color for new pixels. - /// A new . - public Image Insert(Image sub, int x, int y, bool? expand = null, double[] background = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(expand), expand); - options.AddIfPresent(nameof(background), background); - - return this.Call("insert", options, sub, x, y) as Image; - } - - /// - /// Invert an image. - /// - /// - /// - /// using Image @out = in.Invert(); - /// - /// - /// A new . - public Image Invert() - { - return this.Call("invert") as Image; - } - - /// - /// Build an inverted look-up table. - /// - /// - /// - /// using Image @out = in.Invertlut(size: int); - /// - /// - /// LUT size to generate. - /// A new . - public Image Invertlut(int? size = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(size), size); - - return this.Call("invertlut", options) as Image; - } - - /// - /// Inverse FFT. - /// - /// - /// - /// using Image @out = in.Invfft(real: bool); - /// - /// - /// Output only the real part of the transform. - /// A new . - public Image Invfft(bool? real = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(real), real); - - return this.Call("invfft", options) as Image; - } - - /// - /// Join a pair of images. - /// - /// - /// - /// using Image @out = in1.Join(in2, direction, expand: bool, shim: int, background: double[], align: Enums.Align); - /// - /// - /// Second input image. - /// Join left-right or up-down. - /// Expand output to hold all of both inputs. - /// Pixels between images. - /// Colour for new pixels. - /// Align on the low, centre or high coordinate edge. - /// A new . - public Image Join(Image in2, Enums.Direction direction, bool? expand = null, int? shim = null, double[] background = null, Enums.Align? align = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(expand), expand); - options.AddIfPresent(nameof(shim), shim); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent(nameof(align), align); - - return this.Call("join", options, in2, direction) as Image; - } - - /// - /// Load JPEG2000 image. - /// - /// - /// - /// using Image @out = NetVips.Image.Jp2kload(filename, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Load this page from the image. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Jp2kload(string filename, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("jp2kload", options, filename) as Image; - } - - /// - /// Load JPEG2000 image. - /// - /// - /// - /// using Image @out = NetVips.Image.Jp2kload(filename, out var flags, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// Load this page from the image. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Jp2kload(string filename, out Enums.ForeignFlags flags, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("jp2kload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load JPEG2000 image. - /// - /// - /// - /// using Image @out = NetVips.Image.Jp2kloadBuffer(buffer, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Load this page from the image. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Jp2kloadBuffer(byte[] buffer, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("jp2kload_buffer", options, buffer) as Image; - } - - /// - /// Load JPEG2000 image. - /// - /// - /// - /// using Image @out = NetVips.Image.Jp2kloadBuffer(buffer, out var flags, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Flags for this file. - /// Load this page from the image. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Jp2kloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("jp2kload_buffer", options, buffer) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load JPEG2000 image. - /// - /// - /// - /// using Image @out = NetVips.Image.Jp2kloadSource(source, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Load this page from the image. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Jp2kloadSource(Source source, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("jp2kload_source", options, source) as Image; - } - - /// - /// Load JPEG2000 image. - /// - /// - /// - /// using Image @out = NetVips.Image.Jp2kloadStream(stream, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Load this page from the image. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Jp2kloadStream(Stream stream, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = Jp2kloadSource(source, page, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load JPEG2000 image. - /// - /// - /// - /// using Image @out = NetVips.Image.Jp2kloadSource(source, out var flags, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Flags for this file. - /// Load this page from the image. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Jp2kloadSource(Source source, out Enums.ForeignFlags flags, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("jp2kload_source", options, source) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load JPEG2000 image. - /// - /// - /// - /// using Image @out = NetVips.Image.Jp2kloadStream(stream, out var flags, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Flags for this file. - /// Load this page from the image. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Jp2kloadStream(Stream stream, out Enums.ForeignFlags flags, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = Jp2kloadSource(source, out flags, page, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Save image in JPEG2000 format. - /// - /// - /// - /// in.Jp2ksave(filename, tileWidth: int, tileHeight: int, lossless: bool, q: int, subsampleMode: Enums.ForeignSubsample, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Filename to load from. - /// Tile width in pixels. - /// Tile height in pixels. - /// Enable lossless compression. - /// Q factor. - /// Select chroma subsample operation mode. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Jp2ksave(string filename, int? tileWidth = null, int? tileHeight = null, bool? lossless = null, int? q = null, Enums.ForeignSubsample? subsampleMode = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent("tile_width", tileWidth); - options.AddIfPresent("tile_height", tileHeight); - options.AddIfPresent(nameof(lossless), lossless); - options.AddIfPresent("Q", q); - options.AddIfPresent("subsample_mode", subsampleMode); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("jp2ksave", options, filename); - } - - /// - /// Save image in JPEG2000 format. - /// - /// - /// - /// byte[] buffer = in.Jp2ksaveBuffer(tileWidth: int, tileHeight: int, lossless: bool, q: int, subsampleMode: Enums.ForeignSubsample, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Tile width in pixels. - /// Tile height in pixels. - /// Enable lossless compression. - /// Q factor. - /// Select chroma subsample operation mode. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - /// An array of bytes. - public byte[] Jp2ksaveBuffer(int? tileWidth = null, int? tileHeight = null, bool? lossless = null, int? q = null, Enums.ForeignSubsample? subsampleMode = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent("tile_width", tileWidth); - options.AddIfPresent("tile_height", tileHeight); - options.AddIfPresent(nameof(lossless), lossless); - options.AddIfPresent("Q", q); - options.AddIfPresent("subsample_mode", subsampleMode); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - return this.Call("jp2ksave_buffer", options) as byte[]; - } - - /// - /// Save image in JPEG2000 format. - /// - /// - /// - /// in.Jp2ksaveTarget(target, tileWidth: int, tileHeight: int, lossless: bool, q: int, subsampleMode: Enums.ForeignSubsample, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Target to save to. - /// Tile width in pixels. - /// Tile height in pixels. - /// Enable lossless compression. - /// Q factor. - /// Select chroma subsample operation mode. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Jp2ksaveTarget(Target target, int? tileWidth = null, int? tileHeight = null, bool? lossless = null, int? q = null, Enums.ForeignSubsample? subsampleMode = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent("tile_width", tileWidth); - options.AddIfPresent("tile_height", tileHeight); - options.AddIfPresent(nameof(lossless), lossless); - options.AddIfPresent("Q", q); - options.AddIfPresent("subsample_mode", subsampleMode); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("jp2ksave_target", options, target); - } - - /// - /// Save image in JPEG2000 format. - /// - /// - /// - /// in.Jp2ksaveStream(stream, tileWidth: int, tileHeight: int, lossless: bool, q: int, subsampleMode: Enums.ForeignSubsample, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Stream to save to. - /// Tile width in pixels. - /// Tile height in pixels. - /// Enable lossless compression. - /// Q factor. - /// Select chroma subsample operation mode. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Jp2ksaveStream(Stream stream, int? tileWidth = null, int? tileHeight = null, bool? lossless = null, int? q = null, Enums.ForeignSubsample? subsampleMode = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - using var target = TargetStream.NewFromStream(stream); - Jp2ksaveTarget(target, tileWidth, tileHeight, lossless, q, subsampleMode, keep, background, pageHeight, profile); - } - - /// - /// Load jpeg from file. - /// - /// - /// - /// using Image @out = NetVips.Image.Jpegload(filename, shrink: int, autorotate: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Shrink factor on load. - /// Rotate image using exif orientation. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Jpegload(string filename, int? shrink = null, bool? autorotate = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(shrink), shrink); - options.AddIfPresent(nameof(autorotate), autorotate); - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("jpegload", options, filename) as Image; - } - - /// - /// Load jpeg from file. - /// - /// - /// - /// using Image @out = NetVips.Image.Jpegload(filename, out var flags, shrink: int, autorotate: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// Shrink factor on load. - /// Rotate image using exif orientation. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Jpegload(string filename, out Enums.ForeignFlags flags, int? shrink = null, bool? autorotate = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(shrink), shrink); - options.AddIfPresent(nameof(autorotate), autorotate); - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("jpegload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load jpeg from buffer. - /// - /// - /// - /// using Image @out = NetVips.Image.JpegloadBuffer(buffer, shrink: int, autorotate: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Shrink factor on load. - /// Rotate image using exif orientation. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image JpegloadBuffer(byte[] buffer, int? shrink = null, bool? autorotate = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(shrink), shrink); - options.AddIfPresent(nameof(autorotate), autorotate); - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("jpegload_buffer", options, buffer) as Image; - } - - /// - /// Load jpeg from buffer. - /// - /// - /// - /// using Image @out = NetVips.Image.JpegloadBuffer(buffer, out var flags, shrink: int, autorotate: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Flags for this file. - /// Shrink factor on load. - /// Rotate image using exif orientation. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image JpegloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, int? shrink = null, bool? autorotate = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(shrink), shrink); - options.AddIfPresent(nameof(autorotate), autorotate); - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("jpegload_buffer", options, buffer) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load image from jpeg source. - /// - /// - /// - /// using Image @out = NetVips.Image.JpegloadSource(source, shrink: int, autorotate: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Shrink factor on load. - /// Rotate image using exif orientation. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image JpegloadSource(Source source, int? shrink = null, bool? autorotate = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(shrink), shrink); - options.AddIfPresent(nameof(autorotate), autorotate); - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("jpegload_source", options, source) as Image; - } - - /// - /// Load image from jpeg stream. - /// - /// - /// - /// using Image @out = NetVips.Image.JpegloadStream(stream, shrink: int, autorotate: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Shrink factor on load. - /// Rotate image using exif orientation. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image JpegloadStream(Stream stream, int? shrink = null, bool? autorotate = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = JpegloadSource(source, shrink, autorotate, unlimited, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load image from jpeg source. - /// - /// - /// - /// using Image @out = NetVips.Image.JpegloadSource(source, out var flags, shrink: int, autorotate: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Flags for this file. - /// Shrink factor on load. - /// Rotate image using exif orientation. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image JpegloadSource(Source source, out Enums.ForeignFlags flags, int? shrink = null, bool? autorotate = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(shrink), shrink); - options.AddIfPresent(nameof(autorotate), autorotate); - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("jpegload_source", options, source) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load image from jpeg stream. - /// - /// - /// - /// using Image @out = NetVips.Image.JpegloadStream(stream, out var flags, shrink: int, autorotate: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Flags for this file. - /// Shrink factor on load. - /// Rotate image using exif orientation. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image JpegloadStream(Stream stream, out Enums.ForeignFlags flags, int? shrink = null, bool? autorotate = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = JpegloadSource(source, out flags, shrink, autorotate, unlimited, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Save image to jpeg file. - /// - /// - /// - /// in.Jpegsave(filename, q: int, optimizeCoding: bool, interlace: bool, trellisQuant: bool, overshootDeringing: bool, optimizeScans: bool, quantTable: int, subsampleMode: Enums.ForeignSubsample, restartInterval: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Filename to save to. - /// Q factor. - /// Compute optimal Huffman coding tables. - /// Generate an interlaced (progressive) jpeg. - /// Apply trellis quantisation to each 8x8 block. - /// Apply overshooting to samples with extreme values. - /// Split spectrum of DCT coefficients into separate scans. - /// Use predefined quantization table with given index. - /// Select chroma subsample operation mode. - /// Add restart markers every specified number of mcu. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Jpegsave(string filename, int? q = null, bool? optimizeCoding = null, bool? interlace = null, bool? trellisQuant = null, bool? overshootDeringing = null, bool? optimizeScans = null, int? quantTable = null, Enums.ForeignSubsample? subsampleMode = null, int? restartInterval = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent("Q", q); - options.AddIfPresent("optimize_coding", optimizeCoding); - options.AddIfPresent(nameof(interlace), interlace); - options.AddIfPresent("trellis_quant", trellisQuant); - options.AddIfPresent("overshoot_deringing", overshootDeringing); - options.AddIfPresent("optimize_scans", optimizeScans); - options.AddIfPresent("quant_table", quantTable); - options.AddIfPresent("subsample_mode", subsampleMode); - options.AddIfPresent("restart_interval", restartInterval); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("jpegsave", options, filename); - } - - /// - /// Save image to jpeg buffer. - /// - /// - /// - /// byte[] buffer = in.JpegsaveBuffer(q: int, optimizeCoding: bool, interlace: bool, trellisQuant: bool, overshootDeringing: bool, optimizeScans: bool, quantTable: int, subsampleMode: Enums.ForeignSubsample, restartInterval: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Q factor. - /// Compute optimal Huffman coding tables. - /// Generate an interlaced (progressive) jpeg. - /// Apply trellis quantisation to each 8x8 block. - /// Apply overshooting to samples with extreme values. - /// Split spectrum of DCT coefficients into separate scans. - /// Use predefined quantization table with given index. - /// Select chroma subsample operation mode. - /// Add restart markers every specified number of mcu. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - /// An array of bytes. - public byte[] JpegsaveBuffer(int? q = null, bool? optimizeCoding = null, bool? interlace = null, bool? trellisQuant = null, bool? overshootDeringing = null, bool? optimizeScans = null, int? quantTable = null, Enums.ForeignSubsample? subsampleMode = null, int? restartInterval = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent("Q", q); - options.AddIfPresent("optimize_coding", optimizeCoding); - options.AddIfPresent(nameof(interlace), interlace); - options.AddIfPresent("trellis_quant", trellisQuant); - options.AddIfPresent("overshoot_deringing", overshootDeringing); - options.AddIfPresent("optimize_scans", optimizeScans); - options.AddIfPresent("quant_table", quantTable); - options.AddIfPresent("subsample_mode", subsampleMode); - options.AddIfPresent("restart_interval", restartInterval); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - return this.Call("jpegsave_buffer", options) as byte[]; - } - - /// - /// Save image to jpeg mime. - /// - /// - /// - /// in.JpegsaveMime(q: int, optimizeCoding: bool, interlace: bool, trellisQuant: bool, overshootDeringing: bool, optimizeScans: bool, quantTable: int, subsampleMode: Enums.ForeignSubsample, restartInterval: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Q factor. - /// Compute optimal Huffman coding tables. - /// Generate an interlaced (progressive) jpeg. - /// Apply trellis quantisation to each 8x8 block. - /// Apply overshooting to samples with extreme values. - /// Split spectrum of DCT coefficients into separate scans. - /// Use predefined quantization table with given index. - /// Select chroma subsample operation mode. - /// Add restart markers every specified number of mcu. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void JpegsaveMime(int? q = null, bool? optimizeCoding = null, bool? interlace = null, bool? trellisQuant = null, bool? overshootDeringing = null, bool? optimizeScans = null, int? quantTable = null, Enums.ForeignSubsample? subsampleMode = null, int? restartInterval = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent("Q", q); - options.AddIfPresent("optimize_coding", optimizeCoding); - options.AddIfPresent(nameof(interlace), interlace); - options.AddIfPresent("trellis_quant", trellisQuant); - options.AddIfPresent("overshoot_deringing", overshootDeringing); - options.AddIfPresent("optimize_scans", optimizeScans); - options.AddIfPresent("quant_table", quantTable); - options.AddIfPresent("subsample_mode", subsampleMode); - options.AddIfPresent("restart_interval", restartInterval); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("jpegsave_mime", options); - } - - /// - /// Save image to jpeg target. - /// - /// - /// - /// in.JpegsaveTarget(target, q: int, optimizeCoding: bool, interlace: bool, trellisQuant: bool, overshootDeringing: bool, optimizeScans: bool, quantTable: int, subsampleMode: Enums.ForeignSubsample, restartInterval: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Target to save to. - /// Q factor. - /// Compute optimal Huffman coding tables. - /// Generate an interlaced (progressive) jpeg. - /// Apply trellis quantisation to each 8x8 block. - /// Apply overshooting to samples with extreme values. - /// Split spectrum of DCT coefficients into separate scans. - /// Use predefined quantization table with given index. - /// Select chroma subsample operation mode. - /// Add restart markers every specified number of mcu. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void JpegsaveTarget(Target target, int? q = null, bool? optimizeCoding = null, bool? interlace = null, bool? trellisQuant = null, bool? overshootDeringing = null, bool? optimizeScans = null, int? quantTable = null, Enums.ForeignSubsample? subsampleMode = null, int? restartInterval = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent("Q", q); - options.AddIfPresent("optimize_coding", optimizeCoding); - options.AddIfPresent(nameof(interlace), interlace); - options.AddIfPresent("trellis_quant", trellisQuant); - options.AddIfPresent("overshoot_deringing", overshootDeringing); - options.AddIfPresent("optimize_scans", optimizeScans); - options.AddIfPresent("quant_table", quantTable); - options.AddIfPresent("subsample_mode", subsampleMode); - options.AddIfPresent("restart_interval", restartInterval); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("jpegsave_target", options, target); - } - - /// - /// Save image to jpeg stream. - /// - /// - /// - /// in.JpegsaveStream(stream, q: int, optimizeCoding: bool, interlace: bool, trellisQuant: bool, overshootDeringing: bool, optimizeScans: bool, quantTable: int, subsampleMode: Enums.ForeignSubsample, restartInterval: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Stream to save to. - /// Q factor. - /// Compute optimal Huffman coding tables. - /// Generate an interlaced (progressive) jpeg. - /// Apply trellis quantisation to each 8x8 block. - /// Apply overshooting to samples with extreme values. - /// Split spectrum of DCT coefficients into separate scans. - /// Use predefined quantization table with given index. - /// Select chroma subsample operation mode. - /// Add restart markers every specified number of mcu. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void JpegsaveStream(Stream stream, int? q = null, bool? optimizeCoding = null, bool? interlace = null, bool? trellisQuant = null, bool? overshootDeringing = null, bool? optimizeScans = null, int? quantTable = null, Enums.ForeignSubsample? subsampleMode = null, int? restartInterval = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - using var target = TargetStream.NewFromStream(stream); - JpegsaveTarget(target, q, optimizeCoding, interlace, trellisQuant, overshootDeringing, optimizeScans, quantTable, subsampleMode, restartInterval, keep, background, pageHeight, profile); - } - - /// - /// Load JPEG-XL image. - /// - /// - /// - /// using Image @out = NetVips.Image.Jxlload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Jxlload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("jxlload", options, filename) as Image; - } - - /// - /// Load JPEG-XL image. - /// - /// - /// - /// using Image @out = NetVips.Image.Jxlload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Jxlload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("jxlload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load JPEG-XL image. - /// - /// - /// - /// using Image @out = NetVips.Image.JxlloadBuffer(buffer, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image JxlloadBuffer(byte[] buffer, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("jxlload_buffer", options, buffer) as Image; - } - - /// - /// Load JPEG-XL image. - /// - /// - /// - /// using Image @out = NetVips.Image.JxlloadBuffer(buffer, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image JxlloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("jxlload_buffer", options, buffer) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load JPEG-XL image. - /// - /// - /// - /// using Image @out = NetVips.Image.JxlloadSource(source, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image JxlloadSource(Source source, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("jxlload_source", options, source) as Image; - } - - /// - /// Load JPEG-XL image. - /// - /// - /// - /// using Image @out = NetVips.Image.JxlloadStream(stream, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image JxlloadStream(Stream stream, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = JxlloadSource(source, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load JPEG-XL image. - /// - /// - /// - /// using Image @out = NetVips.Image.JxlloadSource(source, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image JxlloadSource(Source source, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("jxlload_source", options, source) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load JPEG-XL image. - /// - /// - /// - /// using Image @out = NetVips.Image.JxlloadStream(stream, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image JxlloadStream(Stream stream, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = JxlloadSource(source, out flags, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Save image in JPEG-XL format. - /// - /// - /// - /// in.Jxlsave(filename, tier: int, distance: double, effort: int, lossless: bool, q: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Filename to load from. - /// Decode speed tier. - /// Target butteraugli distance. - /// Encoding effort. - /// Enable lossless compression. - /// Quality factor. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Jxlsave(string filename, int? tier = null, double? distance = null, int? effort = null, bool? lossless = null, int? q = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(tier), tier); - options.AddIfPresent(nameof(distance), distance); - options.AddIfPresent(nameof(effort), effort); - options.AddIfPresent(nameof(lossless), lossless); - options.AddIfPresent("Q", q); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("jxlsave", options, filename); - } - - /// - /// Save image in JPEG-XL format. - /// - /// - /// - /// byte[] buffer = in.JxlsaveBuffer(tier: int, distance: double, effort: int, lossless: bool, q: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Decode speed tier. - /// Target butteraugli distance. - /// Encoding effort. - /// Enable lossless compression. - /// Quality factor. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - /// An array of bytes. - public byte[] JxlsaveBuffer(int? tier = null, double? distance = null, int? effort = null, bool? lossless = null, int? q = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(tier), tier); - options.AddIfPresent(nameof(distance), distance); - options.AddIfPresent(nameof(effort), effort); - options.AddIfPresent(nameof(lossless), lossless); - options.AddIfPresent("Q", q); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - return this.Call("jxlsave_buffer", options) as byte[]; - } - - /// - /// Save image in JPEG-XL format. - /// - /// - /// - /// in.JxlsaveTarget(target, tier: int, distance: double, effort: int, lossless: bool, q: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Target to save to. - /// Decode speed tier. - /// Target butteraugli distance. - /// Encoding effort. - /// Enable lossless compression. - /// Quality factor. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void JxlsaveTarget(Target target, int? tier = null, double? distance = null, int? effort = null, bool? lossless = null, int? q = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(tier), tier); - options.AddIfPresent(nameof(distance), distance); - options.AddIfPresent(nameof(effort), effort); - options.AddIfPresent(nameof(lossless), lossless); - options.AddIfPresent("Q", q); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("jxlsave_target", options, target); - } - - /// - /// Save image in JPEG-XL format. - /// - /// - /// - /// in.JxlsaveStream(stream, tier: int, distance: double, effort: int, lossless: bool, q: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Stream to save to. - /// Decode speed tier. - /// Target butteraugli distance. - /// Encoding effort. - /// Enable lossless compression. - /// Quality factor. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void JxlsaveStream(Stream stream, int? tier = null, double? distance = null, int? effort = null, bool? lossless = null, int? q = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - using var target = TargetStream.NewFromStream(stream); - JxlsaveTarget(target, tier, distance, effort, lossless, q, keep, background, pageHeight, profile); - } - - /// - /// Transform float Lab to LabQ coding. - /// - /// - /// - /// using Image @out = in.Lab2LabQ(); - /// - /// - /// A new . - public Image Lab2LabQ() - { - return this.Call("Lab2LabQ") as Image; - } - - /// - /// Transform float Lab to signed short. - /// - /// - /// - /// using Image @out = in.Lab2LabS(); - /// - /// - /// A new . - public Image Lab2LabS() - { - return this.Call("Lab2LabS") as Image; - } - - /// - /// Transform Lab to LCh. - /// - /// - /// - /// using Image @out = in.Lab2LCh(); - /// - /// - /// A new . - public Image Lab2LCh() - { - return this.Call("Lab2LCh") as Image; - } - - /// - /// Transform CIELAB to XYZ. - /// - /// - /// - /// using Image @out = in.Lab2XYZ(temp: double[]); - /// - /// - /// Color temperature. - /// A new . - public Image Lab2XYZ(double[] temp = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(temp), temp); - - return this.Call("Lab2XYZ", options) as Image; - } - - /// - /// Label regions in an image. - /// - /// - /// - /// using Image mask = in.Labelregions(); - /// - /// - /// A new . - public Image Labelregions() - { - return this.Call("labelregions") as Image; - } - - /// - /// Label regions in an image. - /// - /// - /// - /// using Image mask = in.Labelregions(out var segments); - /// - /// - /// Number of discrete contiguous regions. - /// A new . - public Image Labelregions(out int segments) - { - var optionalOutput = new VOption - { - {"segments", true} - }; - - var results = this.Call("labelregions", optionalOutput) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - segments = opts?["segments"] is int out1 ? out1 : 0; - - return finalResult; - } - - /// - /// Unpack a LabQ image to float Lab. - /// - /// - /// - /// using Image @out = in.LabQ2Lab(); - /// - /// - /// A new . - public Image LabQ2Lab() - { - return this.Call("LabQ2Lab") as Image; - } - - /// - /// Unpack a LabQ image to short Lab. - /// - /// - /// - /// using Image @out = in.LabQ2LabS(); - /// - /// - /// A new . - public Image LabQ2LabS() - { - return this.Call("LabQ2LabS") as Image; - } - - /// - /// Convert a LabQ image to sRGB. - /// - /// - /// - /// using Image @out = in.LabQ2sRGB(); - /// - /// - /// A new . - public Image LabQ2sRGB() - { - return this.Call("LabQ2sRGB") as Image; - } - - /// - /// Transform signed short Lab to float. - /// - /// - /// - /// using Image @out = in.LabS2Lab(); - /// - /// - /// A new . - public Image LabS2Lab() - { - return this.Call("LabS2Lab") as Image; - } - - /// - /// Transform short Lab to LabQ coding. - /// - /// - /// - /// using Image @out = in.LabS2LabQ(); - /// - /// - /// A new . - public Image LabS2LabQ() - { - return this.Call("LabS2LabQ") as Image; - } - - /// - /// Transform LCh to CMC. - /// - /// - /// - /// using Image @out = in.LCh2CMC(); - /// - /// - /// A new . - public Image LCh2CMC() - { - return this.Call("LCh2CMC") as Image; - } - - /// - /// Transform LCh to Lab. - /// - /// - /// - /// using Image @out = in.LCh2Lab(); - /// - /// - /// A new . - public Image LCh2Lab() - { - return this.Call("LCh2Lab") as Image; - } - - /// - /// Calculate (a * in + b). - /// - /// - /// - /// using Image @out = in.Linear(a, b, uchar: bool); - /// - /// - /// Multiply by this. - /// Add this. - /// Output should be uchar. - /// A new . - public Image Linear(double[] a, double[] b, bool? uchar = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(uchar), uchar); - - return this.Call("linear", options, a, b) as Image; - } - - /// - /// Cache an image as a set of lines. - /// - /// - /// - /// using Image @out = in.Linecache(tileHeight: int, access: Enums.Access, threaded: bool, persistent: bool); - /// - /// - /// Tile height in pixels. - /// Expected access pattern. - /// Allow threaded access. - /// Keep cache between evaluations. - /// A new . - public Image Linecache(int? tileHeight = null, Enums.Access? access = null, bool? threaded = null, bool? persistent = null) - { - var options = new VOption(); - - options.AddIfPresent("tile_height", tileHeight); - options.AddIfPresent(nameof(access), access); - options.AddIfPresent(nameof(threaded), threaded); - options.AddIfPresent(nameof(persistent), persistent); - - return this.Call("linecache", options) as Image; - } - - /// - /// Make a Laplacian of Gaussian image. - /// - /// - /// - /// using Image @out = NetVips.Image.Logmat(sigma, minAmpl, separable: bool, precision: Enums.Precision); - /// - /// - /// Radius of Gaussian. - /// Minimum amplitude of Gaussian. - /// Generate separable Gaussian. - /// Generate with this precision. - /// A new . - public static Image Logmat(double sigma, double minAmpl, bool? separable = null, Enums.Precision? precision = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(separable), separable); - options.AddIfPresent(nameof(precision), precision); - - return Operation.Call("logmat", options, sigma, minAmpl) as Image; - } - - /// - /// Load file with ImageMagick. - /// - /// - /// - /// using Image @out = NetVips.Image.Magickload(filename, density: string, page: int, n: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Canvas resolution for rendering vector formats like SVG. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Magickload(string filename, string density = null, int? page = null, int? n = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(density), density); - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("magickload", options, filename) as Image; - } - - /// - /// Load file with ImageMagick. - /// - /// - /// - /// using Image @out = NetVips.Image.Magickload(filename, out var flags, density: string, page: int, n: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// Canvas resolution for rendering vector formats like SVG. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Magickload(string filename, out Enums.ForeignFlags flags, string density = null, int? page = null, int? n = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(density), density); - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("magickload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load buffer with ImageMagick. - /// - /// - /// - /// using Image @out = NetVips.Image.MagickloadBuffer(buffer, density: string, page: int, n: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Canvas resolution for rendering vector formats like SVG. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image MagickloadBuffer(byte[] buffer, string density = null, int? page = null, int? n = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(density), density); - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("magickload_buffer", options, buffer) as Image; - } - - /// - /// Load buffer with ImageMagick. - /// - /// - /// - /// using Image @out = NetVips.Image.MagickloadBuffer(buffer, out var flags, density: string, page: int, n: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Flags for this file. - /// Canvas resolution for rendering vector formats like SVG. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image MagickloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, string density = null, int? page = null, int? n = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(density), density); - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("magickload_buffer", options, buffer) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Save file with ImageMagick. - /// - /// - /// - /// in.Magicksave(filename, format: string, quality: int, optimizeGifFrames: bool, optimizeGifTransparency: bool, bitdepth: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Filename to save to. - /// Format to save in. - /// Quality to use. - /// Apply GIF frames optimization. - /// Apply GIF transparency optimization. - /// Number of bits per pixel. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Magicksave(string filename, string format = null, int? quality = null, bool? optimizeGifFrames = null, bool? optimizeGifTransparency = null, int? bitdepth = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(format), format); - options.AddIfPresent(nameof(quality), quality); - options.AddIfPresent("optimize_gif_frames", optimizeGifFrames); - options.AddIfPresent("optimize_gif_transparency", optimizeGifTransparency); - options.AddIfPresent(nameof(bitdepth), bitdepth); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("magicksave", options, filename); - } - - /// - /// Save image to magick buffer. - /// - /// - /// - /// byte[] buffer = in.MagicksaveBuffer(format: string, quality: int, optimizeGifFrames: bool, optimizeGifTransparency: bool, bitdepth: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Format to save in. - /// Quality to use. - /// Apply GIF frames optimization. - /// Apply GIF transparency optimization. - /// Number of bits per pixel. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - /// An array of bytes. - public byte[] MagicksaveBuffer(string format = null, int? quality = null, bool? optimizeGifFrames = null, bool? optimizeGifTransparency = null, int? bitdepth = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(format), format); - options.AddIfPresent(nameof(quality), quality); - options.AddIfPresent("optimize_gif_frames", optimizeGifFrames); - options.AddIfPresent("optimize_gif_transparency", optimizeGifTransparency); - options.AddIfPresent(nameof(bitdepth), bitdepth); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - return this.Call("magicksave_buffer", options) as byte[]; - } - - /// - /// Resample with a map image. - /// - /// - /// - /// using Image @out = in.Mapim(index, interpolate: GObject, background: double[], premultiplied: bool, extend: Enums.Extend); - /// - /// - /// Index pixels with this. - /// Interpolate pixels with this. - /// Background value. - /// Images have premultiplied alpha. - /// How to generate the extra pixels. - /// A new . - public Image Mapim(Image index, GObject interpolate = null, double[] background = null, bool? premultiplied = null, Enums.Extend? extend = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(interpolate), interpolate); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent(nameof(premultiplied), premultiplied); - options.AddIfPresent(nameof(extend), extend); - - return this.Call("mapim", options, index) as Image; - } - - /// - /// Map an image though a lut. - /// - /// - /// - /// using Image @out = in.Maplut(lut, band: int); - /// - /// - /// Look-up table image. - /// Apply one-band lut to this band of in. - /// A new . - public Image Maplut(Image lut, int? band = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(band), band); - - return this.Call("maplut", options, lut) as Image; - } - - /// - /// Make a butterworth filter. - /// - /// - /// - /// using Image @out = NetVips.Image.MaskButterworth(width, height, order, frequencyCutoff, amplitudeCutoff, uchar: bool, nodc: bool, reject: bool, optical: bool); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Filter order. - /// Frequency cutoff. - /// Amplitude cutoff. - /// Output an unsigned char image. - /// Remove DC component. - /// Invert the sense of the filter. - /// Rotate quadrants to optical space. - /// A new . - public static Image MaskButterworth(int width, int height, double order, double frequencyCutoff, double amplitudeCutoff, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(uchar), uchar); - options.AddIfPresent(nameof(nodc), nodc); - options.AddIfPresent(nameof(reject), reject); - options.AddIfPresent(nameof(optical), optical); - - return Operation.Call("mask_butterworth", options, width, height, order, frequencyCutoff, amplitudeCutoff) as Image; - } - - /// - /// Make a butterworth_band filter. - /// - /// - /// - /// using Image @out = NetVips.Image.MaskButterworthBand(width, height, order, frequencyCutoffX, frequencyCutoffY, radius, amplitudeCutoff, uchar: bool, nodc: bool, reject: bool, optical: bool); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Filter order. - /// Frequency cutoff x. - /// Frequency cutoff y. - /// Radius of circle. - /// Amplitude cutoff. - /// Output an unsigned char image. - /// Remove DC component. - /// Invert the sense of the filter. - /// Rotate quadrants to optical space. - /// A new . - public static Image MaskButterworthBand(int width, int height, double order, double frequencyCutoffX, double frequencyCutoffY, double radius, double amplitudeCutoff, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(uchar), uchar); - options.AddIfPresent(nameof(nodc), nodc); - options.AddIfPresent(nameof(reject), reject); - options.AddIfPresent(nameof(optical), optical); - - return Operation.Call("mask_butterworth_band", options, width, height, order, frequencyCutoffX, frequencyCutoffY, radius, amplitudeCutoff) as Image; - } - - /// - /// Make a butterworth ring filter. - /// - /// - /// - /// using Image @out = NetVips.Image.MaskButterworthRing(width, height, order, frequencyCutoff, amplitudeCutoff, ringwidth, uchar: bool, nodc: bool, reject: bool, optical: bool); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Filter order. - /// Frequency cutoff. - /// Amplitude cutoff. - /// Ringwidth. - /// Output an unsigned char image. - /// Remove DC component. - /// Invert the sense of the filter. - /// Rotate quadrants to optical space. - /// A new . - public static Image MaskButterworthRing(int width, int height, double order, double frequencyCutoff, double amplitudeCutoff, double ringwidth, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(uchar), uchar); - options.AddIfPresent(nameof(nodc), nodc); - options.AddIfPresent(nameof(reject), reject); - options.AddIfPresent(nameof(optical), optical); - - return Operation.Call("mask_butterworth_ring", options, width, height, order, frequencyCutoff, amplitudeCutoff, ringwidth) as Image; - } - - /// - /// Make fractal filter. - /// - /// - /// - /// using Image @out = NetVips.Image.MaskFractal(width, height, fractalDimension, uchar: bool, nodc: bool, reject: bool, optical: bool); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Fractal dimension. - /// Output an unsigned char image. - /// Remove DC component. - /// Invert the sense of the filter. - /// Rotate quadrants to optical space. - /// A new . - public static Image MaskFractal(int width, int height, double fractalDimension, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(uchar), uchar); - options.AddIfPresent(nameof(nodc), nodc); - options.AddIfPresent(nameof(reject), reject); - options.AddIfPresent(nameof(optical), optical); - - return Operation.Call("mask_fractal", options, width, height, fractalDimension) as Image; - } - - /// - /// Make a gaussian filter. - /// - /// - /// - /// using Image @out = NetVips.Image.MaskGaussian(width, height, frequencyCutoff, amplitudeCutoff, uchar: bool, nodc: bool, reject: bool, optical: bool); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Frequency cutoff. - /// Amplitude cutoff. - /// Output an unsigned char image. - /// Remove DC component. - /// Invert the sense of the filter. - /// Rotate quadrants to optical space. - /// A new . - public static Image MaskGaussian(int width, int height, double frequencyCutoff, double amplitudeCutoff, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(uchar), uchar); - options.AddIfPresent(nameof(nodc), nodc); - options.AddIfPresent(nameof(reject), reject); - options.AddIfPresent(nameof(optical), optical); - - return Operation.Call("mask_gaussian", options, width, height, frequencyCutoff, amplitudeCutoff) as Image; - } - - /// - /// Make a gaussian filter. - /// - /// - /// - /// using Image @out = NetVips.Image.MaskGaussianBand(width, height, frequencyCutoffX, frequencyCutoffY, radius, amplitudeCutoff, uchar: bool, nodc: bool, reject: bool, optical: bool); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Frequency cutoff x. - /// Frequency cutoff y. - /// Radius of circle. - /// Amplitude cutoff. - /// Output an unsigned char image. - /// Remove DC component. - /// Invert the sense of the filter. - /// Rotate quadrants to optical space. - /// A new . - public static Image MaskGaussianBand(int width, int height, double frequencyCutoffX, double frequencyCutoffY, double radius, double amplitudeCutoff, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(uchar), uchar); - options.AddIfPresent(nameof(nodc), nodc); - options.AddIfPresent(nameof(reject), reject); - options.AddIfPresent(nameof(optical), optical); - - return Operation.Call("mask_gaussian_band", options, width, height, frequencyCutoffX, frequencyCutoffY, radius, amplitudeCutoff) as Image; - } - - /// - /// Make a gaussian ring filter. - /// - /// - /// - /// using Image @out = NetVips.Image.MaskGaussianRing(width, height, frequencyCutoff, amplitudeCutoff, ringwidth, uchar: bool, nodc: bool, reject: bool, optical: bool); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Frequency cutoff. - /// Amplitude cutoff. - /// Ringwidth. - /// Output an unsigned char image. - /// Remove DC component. - /// Invert the sense of the filter. - /// Rotate quadrants to optical space. - /// A new . - public static Image MaskGaussianRing(int width, int height, double frequencyCutoff, double amplitudeCutoff, double ringwidth, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(uchar), uchar); - options.AddIfPresent(nameof(nodc), nodc); - options.AddIfPresent(nameof(reject), reject); - options.AddIfPresent(nameof(optical), optical); - - return Operation.Call("mask_gaussian_ring", options, width, height, frequencyCutoff, amplitudeCutoff, ringwidth) as Image; - } - - /// - /// Make an ideal filter. - /// - /// - /// - /// using Image @out = NetVips.Image.MaskIdeal(width, height, frequencyCutoff, uchar: bool, nodc: bool, reject: bool, optical: bool); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Frequency cutoff. - /// Output an unsigned char image. - /// Remove DC component. - /// Invert the sense of the filter. - /// Rotate quadrants to optical space. - /// A new . - public static Image MaskIdeal(int width, int height, double frequencyCutoff, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(uchar), uchar); - options.AddIfPresent(nameof(nodc), nodc); - options.AddIfPresent(nameof(reject), reject); - options.AddIfPresent(nameof(optical), optical); - - return Operation.Call("mask_ideal", options, width, height, frequencyCutoff) as Image; - } - - /// - /// Make an ideal band filter. - /// - /// - /// - /// using Image @out = NetVips.Image.MaskIdealBand(width, height, frequencyCutoffX, frequencyCutoffY, radius, uchar: bool, nodc: bool, reject: bool, optical: bool); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Frequency cutoff x. - /// Frequency cutoff y. - /// Radius of circle. - /// Output an unsigned char image. - /// Remove DC component. - /// Invert the sense of the filter. - /// Rotate quadrants to optical space. - /// A new . - public static Image MaskIdealBand(int width, int height, double frequencyCutoffX, double frequencyCutoffY, double radius, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(uchar), uchar); - options.AddIfPresent(nameof(nodc), nodc); - options.AddIfPresent(nameof(reject), reject); - options.AddIfPresent(nameof(optical), optical); - - return Operation.Call("mask_ideal_band", options, width, height, frequencyCutoffX, frequencyCutoffY, radius) as Image; - } - - /// - /// Make an ideal ring filter. - /// - /// - /// - /// using Image @out = NetVips.Image.MaskIdealRing(width, height, frequencyCutoff, ringwidth, uchar: bool, nodc: bool, reject: bool, optical: bool); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Frequency cutoff. - /// Ringwidth. - /// Output an unsigned char image. - /// Remove DC component. - /// Invert the sense of the filter. - /// Rotate quadrants to optical space. - /// A new . - public static Image MaskIdealRing(int width, int height, double frequencyCutoff, double ringwidth, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(uchar), uchar); - options.AddIfPresent(nameof(nodc), nodc); - options.AddIfPresent(nameof(reject), reject); - options.AddIfPresent(nameof(optical), optical); - - return Operation.Call("mask_ideal_ring", options, width, height, frequencyCutoff, ringwidth) as Image; - } - - /// - /// First-order match of two images. - /// - /// - /// - /// using Image @out = ref.Match(sec, xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, hwindow: int, harea: int, search: bool, interpolate: GObject); - /// - /// - /// Secondary image. - /// Position of first reference tie-point. - /// Position of first reference tie-point. - /// Position of first secondary tie-point. - /// Position of first secondary tie-point. - /// Position of second reference tie-point. - /// Position of second reference tie-point. - /// Position of second secondary tie-point. - /// Position of second secondary tie-point. - /// Half window size. - /// Half area size. - /// Search to improve tie-points. - /// Interpolate pixels with this. - /// A new . - public Image Match(Image sec, int xr1, int yr1, int xs1, int ys1, int xr2, int yr2, int xs2, int ys2, int? hwindow = null, int? harea = null, bool? search = null, GObject interpolate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(hwindow), hwindow); - options.AddIfPresent(nameof(harea), harea); - options.AddIfPresent(nameof(search), search); - options.AddIfPresent(nameof(interpolate), interpolate); - - return this.Call("match", options, sec, xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2) as Image; - } - - /// - /// Apply a math operation to an image. - /// - /// - /// - /// using Image @out = in.Math(math); - /// - /// - /// Math to perform. - /// A new . - public Image Math(Enums.OperationMath math) - { - return this.Call("math", math) as Image; - } - - /// - /// Binary math operations. - /// - /// - /// - /// using Image @out = left.Math2(right, math2); - /// - /// - /// Right-hand image argument. - /// Math to perform. - /// A new . - public Image Math2(Image right, Enums.OperationMath2 math2) - { - return this.Call("math2", right, math2) as Image; - } - - /// - /// Binary math operations with a constant. - /// - /// - /// - /// using Image @out = in.Math2Const(math2, c); - /// - /// - /// Math to perform. - /// Array of constants. - /// A new . - public Image Math2Const(Enums.OperationMath2 math2, double[] c) - { - return this.Call("math2_const", math2, c) as Image; - } - - /// - /// Load mat from file. - /// - /// - /// - /// using Image @out = NetVips.Image.Matload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Matload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("matload", options, filename) as Image; - } - - /// - /// Load mat from file. - /// - /// - /// - /// using Image @out = NetVips.Image.Matload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Matload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("matload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Invert an matrix. - /// - /// - /// - /// using Image @out = in.Matrixinvert(); - /// - /// - /// A new . - public Image Matrixinvert() - { - return this.Call("matrixinvert") as Image; - } - - /// - /// Load matrix. - /// - /// - /// - /// using Image @out = NetVips.Image.Matrixload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Matrixload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("matrixload", options, filename) as Image; - } - - /// - /// Load matrix. - /// - /// - /// - /// using Image @out = NetVips.Image.Matrixload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Matrixload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("matrixload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load matrix. - /// - /// - /// - /// using Image @out = NetVips.Image.MatrixloadSource(source, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image MatrixloadSource(Source source, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("matrixload_source", options, source) as Image; - } - - /// - /// Load matrix. - /// - /// - /// - /// using Image @out = NetVips.Image.MatrixloadStream(stream, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image MatrixloadStream(Stream stream, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = MatrixloadSource(source, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load matrix. - /// - /// - /// - /// using Image @out = NetVips.Image.MatrixloadSource(source, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image MatrixloadSource(Source source, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("matrixload_source", options, source) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load matrix. - /// - /// - /// - /// using Image @out = NetVips.Image.MatrixloadStream(stream, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image MatrixloadStream(Stream stream, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + #region auto-generated functions + + /// + /// Absolute value of an image. + /// + /// + /// + /// using Image @out = in.Abs(); + /// + /// + /// A new . + public Image Abs() + { + return this.Call("abs") as Image; + } + + /// + /// Add two images. + /// + /// + /// + /// using Image @out = left.Add(right); + /// + /// + /// Right-hand image argument. + /// A new . + public Image Add(Image right) + { + return this.Call("add", right) as Image; + } + + /// + /// Affine transform of an image. + /// + /// + /// + /// using Image @out = in.Affine(matrix, interpolate: GObject, oarea: int[], odx: double, ody: double, idx: double, idy: double, background: double[], premultiplied: bool, extend: Enums.Extend); + /// + /// + /// Transformation matrix. + /// Interpolate pixels with this. + /// Area of output to generate. + /// Horizontal output displacement. + /// Vertical output displacement. + /// Horizontal input displacement. + /// Vertical input displacement. + /// Background value. + /// Images have premultiplied alpha. + /// How to generate the extra pixels. + /// A new . + public Image Affine(double[] matrix, GObject interpolate = null, int[] oarea = null, double? odx = null, double? ody = null, double? idx = null, double? idy = null, double[] background = null, bool? premultiplied = null, Enums.Extend? extend = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(interpolate), interpolate); + options.AddIfPresent(nameof(oarea), oarea); + options.AddIfPresent(nameof(odx), odx); + options.AddIfPresent(nameof(ody), ody); + options.AddIfPresent(nameof(idx), idx); + options.AddIfPresent(nameof(idy), idy); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent(nameof(premultiplied), premultiplied); + options.AddIfPresent(nameof(extend), extend); + + return this.Call("affine", options, matrix) as Image; + } + + /// + /// Load an Analyze6 image. + /// + /// + /// + /// using Image @out = NetVips.Image.Analyzeload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Analyzeload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("analyzeload", options, filename) as Image; + } + + /// + /// Load an Analyze6 image. + /// + /// + /// + /// using Image @out = NetVips.Image.Analyzeload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Analyzeload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("analyzeload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Join an array of images. + /// + /// + /// + /// using Image @out = NetVips.Image.Arrayjoin(@in, across: int, shim: int, background: double[], halign: Enums.Align, valign: Enums.Align, hspacing: int, vspacing: int); + /// + /// + /// Array of input images. + /// Number of images across grid. + /// Pixels between images. + /// Colour for new pixels. + /// Align on the left, centre or right. + /// Align on the top, centre or bottom. + /// Horizontal spacing between images. + /// Vertical spacing between images. + /// A new . + public static Image Arrayjoin(Image[] @in, int? across = null, int? shim = null, double[] background = null, Enums.Align? halign = null, Enums.Align? valign = null, int? hspacing = null, int? vspacing = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(across), across); + options.AddIfPresent(nameof(shim), shim); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent(nameof(halign), halign); + options.AddIfPresent(nameof(valign), valign); + options.AddIfPresent(nameof(hspacing), hspacing); + options.AddIfPresent(nameof(vspacing), vspacing); + + return Operation.Call("arrayjoin", options, new object[] { @in }) as Image; + } + + /// + /// Autorotate image by exif tag. + /// + /// + /// + /// using Image @out = in.Autorot(); + /// + /// + /// A new . + public Image Autorot() + { + return this.Call("autorot") as Image; + } + + /// + /// Autorotate image by exif tag. + /// + /// + /// + /// using Image @out = in.Autorot(out var angle); + /// + /// + /// Angle image was rotated by. + /// A new . + public Image Autorot(out Enums.Angle angle) + { + var optionalOutput = new VOption { - var source = SourceStream.NewFromStream(stream); - var image = MatrixloadSource(source, out flags, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Print matrix. - /// - /// - /// - /// in.Matrixprint(keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Matrixprint(Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + {"angle", true} + }; + + var results = this.Call("autorot", optionalOutput) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + angle = (Enums.Angle)opts?["angle"]; + + return finalResult; + } + + /// + /// Autorotate image by exif tag. + /// + /// + /// + /// using Image @out = in.Autorot(out var angle, out var flip); + /// + /// + /// Angle image was rotated by. + /// Whether the image was flipped or not. + /// A new . + public Image Autorot(out Enums.Angle angle, out bool flip) + { + var optionalOutput = new VOption { - var options = new VOption(); - - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("matrixprint", options); - } - - /// - /// Save image to matrix. - /// - /// - /// - /// in.Matrixsave(filename, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Filename to save to. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Matrixsave(string filename, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("matrixsave", options, filename); - } - - /// - /// Save image to matrix. - /// - /// - /// - /// in.MatrixsaveTarget(target, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Target to save to. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void MatrixsaveTarget(Target target, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("matrixsave_target", options, target); - } - - /// - /// Save image to matrix. - /// - /// - /// - /// in.MatrixsaveStream(stream, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Stream to save to. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void MatrixsaveStream(Stream stream, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - using var target = TargetStream.NewFromStream(stream); - MatrixsaveTarget(target, keep, background, pageHeight, profile); - } - - /// - /// Find image maximum. - /// - /// - /// - /// double @out = in.Max(size: int); - /// - /// - /// Number of maximum values to find. - /// A double. - public double Max(int? size = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(size), size); - - return this.Call("max", options) is double result ? result : 0d; - } - - /// - /// Find image maximum. - /// - /// - /// - /// double @out = in.Max(out var x, size: int); - /// - /// - /// Horizontal position of maximum. - /// Number of maximum values to find. - /// A double. - public double Max(out int x, int? size = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(size), size); - - options.Add("x", true); - - var results = this.Call("max", options) as object[]; - var finalResult = results?[0] is double result ? result : 0d; - var opts = results?[1] as VOption; - x = opts?["x"] is int out1 ? out1 : 0; - - return finalResult; - } - - /// - /// Find image maximum. - /// - /// - /// - /// double @out = in.Max(out var x, out var y, size: int); - /// - /// - /// Horizontal position of maximum. - /// Vertical position of maximum. - /// Number of maximum values to find. - /// A double. - public double Max(out int x, out int y, int? size = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(size), size); - - options.Add("x", true); - options.Add("y", true); - - var results = this.Call("max", options) as object[]; - var finalResult = results?[0] is double result ? result : 0d; - var opts = results?[1] as VOption; - x = opts?["x"] is int out1 ? out1 : 0; - y = opts?["y"] is int out2 ? out2 : 0; - - return finalResult; - } - - /// - /// Find image maximum. - /// - /// - /// - /// double @out = in.Max(out var x, out var y, out var outArray, size: int); - /// - /// - /// Horizontal position of maximum. - /// Vertical position of maximum. - /// Array of output values. - /// Number of maximum values to find. - /// A double. - public double Max(out int x, out int y, out double[] outArray, int? size = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(size), size); - - options.Add("x", true); - options.Add("y", true); - options.Add("out_array", true); - - var results = this.Call("max", options) as object[]; - var finalResult = results?[0] is double result ? result : 0d; - var opts = results?[1] as VOption; - x = opts?["x"] is int out1 ? out1 : 0; - y = opts?["y"] is int out2 ? out2 : 0; - outArray = opts?["out_array"] as double[]; - - return finalResult; - } - - /// - /// Find image maximum. - /// - /// - /// - /// double @out = in.Max(out var x, out var y, out var outArray, out var xArray, size: int); - /// - /// - /// Horizontal position of maximum. - /// Vertical position of maximum. - /// Array of output values. - /// Array of horizontal positions. - /// Number of maximum values to find. - /// A double. - public double Max(out int x, out int y, out double[] outArray, out int[] xArray, int? size = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(size), size); - - options.Add("x", true); - options.Add("y", true); - options.Add("out_array", true); - options.Add("x_array", true); - - var results = this.Call("max", options) as object[]; - var finalResult = results?[0] is double result ? result : 0d; - var opts = results?[1] as VOption; - x = opts?["x"] is int out1 ? out1 : 0; - y = opts?["y"] is int out2 ? out2 : 0; - outArray = opts?["out_array"] as double[]; - xArray = opts?["x_array"] as int[]; - - return finalResult; - } - - /// - /// Find image maximum. - /// - /// - /// - /// double @out = in.Max(out var x, out var y, out var outArray, out var xArray, out var yArray, size: int); - /// - /// - /// Horizontal position of maximum. - /// Vertical position of maximum. - /// Array of output values. - /// Array of horizontal positions. - /// Array of vertical positions. - /// Number of maximum values to find. - /// A double. - public double Max(out int x, out int y, out double[] outArray, out int[] xArray, out int[] yArray, int? size = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(size), size); - - options.Add("x", true); - options.Add("y", true); - options.Add("out_array", true); - options.Add("x_array", true); - options.Add("y_array", true); - - var results = this.Call("max", options) as object[]; - var finalResult = results?[0] is double result ? result : 0d; - var opts = results?[1] as VOption; - x = opts?["x"] is int out1 ? out1 : 0; - y = opts?["y"] is int out2 ? out2 : 0; - outArray = opts?["out_array"] as double[]; - xArray = opts?["x_array"] as int[]; - yArray = opts?["y_array"] as int[]; - - return finalResult; - } - - /// - /// Measure a set of patches on a color chart. - /// - /// - /// - /// using Image @out = in.Measure(h, v, left: int, top: int, width: int, height: int); - /// - /// - /// Number of patches across chart. - /// Number of patches down chart. - /// Left edge of extract area. - /// Top edge of extract area. - /// Width of extract area. - /// Height of extract area. - /// A new . - public Image Measure(int h, int v, int? left = null, int? top = null, int? width = null, int? height = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(left), left); - options.AddIfPresent(nameof(top), top); - options.AddIfPresent(nameof(width), width); - options.AddIfPresent(nameof(height), height); - - return this.Call("measure", options, h, v) as Image; - } - - /// - /// Merge two images. - /// - /// - /// - /// using Image @out = ref.Merge(sec, direction, dx, dy, mblend: int); - /// - /// - /// Secondary image. - /// Horizontal or vertical merge. - /// Horizontal displacement from sec to ref. - /// Vertical displacement from sec to ref. - /// Maximum blend size. - /// A new . - public Image Merge(Image sec, Enums.Direction direction, int dx, int dy, int? mblend = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(mblend), mblend); - - return this.Call("merge", options, sec, direction, dx, dy) as Image; - } - - /// - /// Find image minimum. - /// - /// - /// - /// double @out = in.Min(size: int); - /// - /// - /// Number of minimum values to find. - /// A double. - public double Min(int? size = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(size), size); - - return this.Call("min", options) is double result ? result : 0d; - } - - /// - /// Find image minimum. - /// - /// - /// - /// double @out = in.Min(out var x, size: int); - /// - /// - /// Horizontal position of minimum. - /// Number of minimum values to find. - /// A double. - public double Min(out int x, int? size = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(size), size); - - options.Add("x", true); - - var results = this.Call("min", options) as object[]; - var finalResult = results?[0] is double result ? result : 0d; - var opts = results?[1] as VOption; - x = opts?["x"] is int out1 ? out1 : 0; - - return finalResult; - } - - /// - /// Find image minimum. - /// - /// - /// - /// double @out = in.Min(out var x, out var y, size: int); - /// - /// - /// Horizontal position of minimum. - /// Vertical position of minimum. - /// Number of minimum values to find. - /// A double. - public double Min(out int x, out int y, int? size = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(size), size); - - options.Add("x", true); - options.Add("y", true); - - var results = this.Call("min", options) as object[]; - var finalResult = results?[0] is double result ? result : 0d; - var opts = results?[1] as VOption; - x = opts?["x"] is int out1 ? out1 : 0; - y = opts?["y"] is int out2 ? out2 : 0; - - return finalResult; - } - - /// - /// Find image minimum. - /// - /// - /// - /// double @out = in.Min(out var x, out var y, out var outArray, size: int); - /// - /// - /// Horizontal position of minimum. - /// Vertical position of minimum. - /// Array of output values. - /// Number of minimum values to find. - /// A double. - public double Min(out int x, out int y, out double[] outArray, int? size = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(size), size); - - options.Add("x", true); - options.Add("y", true); - options.Add("out_array", true); - - var results = this.Call("min", options) as object[]; - var finalResult = results?[0] is double result ? result : 0d; - var opts = results?[1] as VOption; - x = opts?["x"] is int out1 ? out1 : 0; - y = opts?["y"] is int out2 ? out2 : 0; - outArray = opts?["out_array"] as double[]; - - return finalResult; - } - - /// - /// Find image minimum. - /// - /// - /// - /// double @out = in.Min(out var x, out var y, out var outArray, out var xArray, size: int); - /// - /// - /// Horizontal position of minimum. - /// Vertical position of minimum. - /// Array of output values. - /// Array of horizontal positions. - /// Number of minimum values to find. - /// A double. - public double Min(out int x, out int y, out double[] outArray, out int[] xArray, int? size = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(size), size); - - options.Add("x", true); - options.Add("y", true); - options.Add("out_array", true); - options.Add("x_array", true); - - var results = this.Call("min", options) as object[]; - var finalResult = results?[0] is double result ? result : 0d; - var opts = results?[1] as VOption; - x = opts?["x"] is int out1 ? out1 : 0; - y = opts?["y"] is int out2 ? out2 : 0; - outArray = opts?["out_array"] as double[]; - xArray = opts?["x_array"] as int[]; - - return finalResult; - } - - /// - /// Find image minimum. - /// - /// - /// - /// double @out = in.Min(out var x, out var y, out var outArray, out var xArray, out var yArray, size: int); - /// - /// - /// Horizontal position of minimum. - /// Vertical position of minimum. - /// Array of output values. - /// Array of horizontal positions. - /// Array of vertical positions. - /// Number of minimum values to find. - /// A double. - public double Min(out int x, out int y, out double[] outArray, out int[] xArray, out int[] yArray, int? size = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(size), size); - - options.Add("x", true); - options.Add("y", true); - options.Add("out_array", true); - options.Add("x_array", true); - options.Add("y_array", true); - - var results = this.Call("min", options) as object[]; - var finalResult = results?[0] is double result ? result : 0d; - var opts = results?[1] as VOption; - x = opts?["x"] is int out1 ? out1 : 0; - y = opts?["y"] is int out2 ? out2 : 0; - outArray = opts?["out_array"] as double[]; - xArray = opts?["x_array"] as int[]; - yArray = opts?["y_array"] as int[]; - - return finalResult; - } - - /// - /// Morphology operation. - /// - /// - /// - /// using Image @out = in.Morph(mask, morph); - /// - /// - /// Input matrix image. - /// Morphological operation to perform. - /// A new . - public Image Morph(Image mask, Enums.OperationMorphology morph) - { - return this.Call("morph", mask, morph) as Image; - } - - /// - /// Mosaic two images. - /// - /// - /// - /// using Image @out = ref.Mosaic(sec, direction, xref, yref, xsec, ysec, hwindow: int, harea: int, mblend: int, bandno: int); - /// - /// - /// Secondary image. - /// Horizontal or vertical mosaic. - /// Position of reference tie-point. - /// Position of reference tie-point. - /// Position of secondary tie-point. - /// Position of secondary tie-point. - /// Half window size. - /// Half area size. - /// Maximum blend size. - /// Band to search for features on. - /// A new . - public Image Mosaic(Image sec, Enums.Direction direction, int xref, int yref, int xsec, int ysec, int? hwindow = null, int? harea = null, int? mblend = null, int? bandno = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(hwindow), hwindow); - options.AddIfPresent(nameof(harea), harea); - options.AddIfPresent(nameof(mblend), mblend); - options.AddIfPresent(nameof(bandno), bandno); - - return this.Call("mosaic", options, sec, direction, xref, yref, xsec, ysec) as Image; - } - - /// - /// Mosaic two images. - /// - /// - /// - /// using Image @out = ref.Mosaic(sec, direction, xref, yref, xsec, ysec, out var dx0, hwindow: int, harea: int, mblend: int, bandno: int); - /// - /// - /// Secondary image. - /// Horizontal or vertical mosaic. - /// Position of reference tie-point. - /// Position of reference tie-point. - /// Position of secondary tie-point. - /// Position of secondary tie-point. - /// Detected integer offset. - /// Half window size. - /// Half area size. - /// Maximum blend size. - /// Band to search for features on. - /// A new . - public Image Mosaic(Image sec, Enums.Direction direction, int xref, int yref, int xsec, int ysec, out int dx0, int? hwindow = null, int? harea = null, int? mblend = null, int? bandno = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(hwindow), hwindow); - options.AddIfPresent(nameof(harea), harea); - options.AddIfPresent(nameof(mblend), mblend); - options.AddIfPresent(nameof(bandno), bandno); - - options.Add("dx0", true); - - var results = this.Call("mosaic", options, sec, direction, xref, yref, xsec, ysec) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - dx0 = opts?["dx0"] is int out1 ? out1 : 0; - - return finalResult; - } - - /// - /// Mosaic two images. - /// - /// - /// - /// using Image @out = ref.Mosaic(sec, direction, xref, yref, xsec, ysec, out var dx0, out var dy0, hwindow: int, harea: int, mblend: int, bandno: int); - /// - /// - /// Secondary image. - /// Horizontal or vertical mosaic. - /// Position of reference tie-point. - /// Position of reference tie-point. - /// Position of secondary tie-point. - /// Position of secondary tie-point. - /// Detected integer offset. - /// Detected integer offset. - /// Half window size. - /// Half area size. - /// Maximum blend size. - /// Band to search for features on. - /// A new . - public Image Mosaic(Image sec, Enums.Direction direction, int xref, int yref, int xsec, int ysec, out int dx0, out int dy0, int? hwindow = null, int? harea = null, int? mblend = null, int? bandno = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(hwindow), hwindow); - options.AddIfPresent(nameof(harea), harea); - options.AddIfPresent(nameof(mblend), mblend); - options.AddIfPresent(nameof(bandno), bandno); - - options.Add("dx0", true); - options.Add("dy0", true); - - var results = this.Call("mosaic", options, sec, direction, xref, yref, xsec, ysec) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - dx0 = opts?["dx0"] is int out1 ? out1 : 0; - dy0 = opts?["dy0"] is int out2 ? out2 : 0; - - return finalResult; - } - - /// - /// Mosaic two images. - /// - /// - /// - /// using Image @out = ref.Mosaic(sec, direction, xref, yref, xsec, ysec, out var dx0, out var dy0, out var scale1, hwindow: int, harea: int, mblend: int, bandno: int); - /// - /// - /// Secondary image. - /// Horizontal or vertical mosaic. - /// Position of reference tie-point. - /// Position of reference tie-point. - /// Position of secondary tie-point. - /// Position of secondary tie-point. - /// Detected integer offset. - /// Detected integer offset. - /// Detected scale. - /// Half window size. - /// Half area size. - /// Maximum blend size. - /// Band to search for features on. - /// A new . - public Image Mosaic(Image sec, Enums.Direction direction, int xref, int yref, int xsec, int ysec, out int dx0, out int dy0, out double scale1, int? hwindow = null, int? harea = null, int? mblend = null, int? bandno = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(hwindow), hwindow); - options.AddIfPresent(nameof(harea), harea); - options.AddIfPresent(nameof(mblend), mblend); - options.AddIfPresent(nameof(bandno), bandno); - - options.Add("dx0", true); - options.Add("dy0", true); - options.Add("scale1", true); - - var results = this.Call("mosaic", options, sec, direction, xref, yref, xsec, ysec) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - dx0 = opts?["dx0"] is int out1 ? out1 : 0; - dy0 = opts?["dy0"] is int out2 ? out2 : 0; - scale1 = opts?["scale1"] is double out3 ? out3 : 0d; - - return finalResult; - } - - /// - /// Mosaic two images. - /// - /// - /// - /// using Image @out = ref.Mosaic(sec, direction, xref, yref, xsec, ysec, out var dx0, out var dy0, out var scale1, out var angle1, hwindow: int, harea: int, mblend: int, bandno: int); - /// - /// - /// Secondary image. - /// Horizontal or vertical mosaic. - /// Position of reference tie-point. - /// Position of reference tie-point. - /// Position of secondary tie-point. - /// Position of secondary tie-point. - /// Detected integer offset. - /// Detected integer offset. - /// Detected scale. - /// Detected rotation. - /// Half window size. - /// Half area size. - /// Maximum blend size. - /// Band to search for features on. - /// A new . - public Image Mosaic(Image sec, Enums.Direction direction, int xref, int yref, int xsec, int ysec, out int dx0, out int dy0, out double scale1, out double angle1, int? hwindow = null, int? harea = null, int? mblend = null, int? bandno = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(hwindow), hwindow); - options.AddIfPresent(nameof(harea), harea); - options.AddIfPresent(nameof(mblend), mblend); - options.AddIfPresent(nameof(bandno), bandno); - - options.Add("dx0", true); - options.Add("dy0", true); - options.Add("scale1", true); - options.Add("angle1", true); - - var results = this.Call("mosaic", options, sec, direction, xref, yref, xsec, ysec) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - dx0 = opts?["dx0"] is int out1 ? out1 : 0; - dy0 = opts?["dy0"] is int out2 ? out2 : 0; - scale1 = opts?["scale1"] is double out3 ? out3 : 0d; - angle1 = opts?["angle1"] is double out4 ? out4 : 0d; - - return finalResult; - } - - /// - /// Mosaic two images. - /// - /// - /// - /// using Image @out = ref.Mosaic(sec, direction, xref, yref, xsec, ysec, out var dx0, out var dy0, out var scale1, out var angle1, out var dy1, hwindow: int, harea: int, mblend: int, bandno: int); - /// - /// - /// Secondary image. - /// Horizontal or vertical mosaic. - /// Position of reference tie-point. - /// Position of reference tie-point. - /// Position of secondary tie-point. - /// Position of secondary tie-point. - /// Detected integer offset. - /// Detected integer offset. - /// Detected scale. - /// Detected rotation. - /// Detected first-order displacement. - /// Half window size. - /// Half area size. - /// Maximum blend size. - /// Band to search for features on. - /// A new . - public Image Mosaic(Image sec, Enums.Direction direction, int xref, int yref, int xsec, int ysec, out int dx0, out int dy0, out double scale1, out double angle1, out double dy1, int? hwindow = null, int? harea = null, int? mblend = null, int? bandno = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(hwindow), hwindow); - options.AddIfPresent(nameof(harea), harea); - options.AddIfPresent(nameof(mblend), mblend); - options.AddIfPresent(nameof(bandno), bandno); - - options.Add("dx0", true); - options.Add("dy0", true); - options.Add("scale1", true); - options.Add("angle1", true); - options.Add("dy1", true); - - var results = this.Call("mosaic", options, sec, direction, xref, yref, xsec, ysec) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - dx0 = opts?["dx0"] is int out1 ? out1 : 0; - dy0 = opts?["dy0"] is int out2 ? out2 : 0; - scale1 = opts?["scale1"] is double out3 ? out3 : 0d; - angle1 = opts?["angle1"] is double out4 ? out4 : 0d; - dy1 = opts?["dy1"] is double out5 ? out5 : 0d; - - return finalResult; - } - - /// - /// Mosaic two images. - /// - /// - /// - /// using Image @out = ref.Mosaic(sec, direction, xref, yref, xsec, ysec, out var dx0, out var dy0, out var scale1, out var angle1, out var dy1, out var dx1, hwindow: int, harea: int, mblend: int, bandno: int); - /// - /// - /// Secondary image. - /// Horizontal or vertical mosaic. - /// Position of reference tie-point. - /// Position of reference tie-point. - /// Position of secondary tie-point. - /// Position of secondary tie-point. - /// Detected integer offset. - /// Detected integer offset. - /// Detected scale. - /// Detected rotation. - /// Detected first-order displacement. - /// Detected first-order displacement. - /// Half window size. - /// Half area size. - /// Maximum blend size. - /// Band to search for features on. - /// A new . - public Image Mosaic(Image sec, Enums.Direction direction, int xref, int yref, int xsec, int ysec, out int dx0, out int dy0, out double scale1, out double angle1, out double dy1, out double dx1, int? hwindow = null, int? harea = null, int? mblend = null, int? bandno = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(hwindow), hwindow); - options.AddIfPresent(nameof(harea), harea); - options.AddIfPresent(nameof(mblend), mblend); - options.AddIfPresent(nameof(bandno), bandno); - - options.Add("dx0", true); - options.Add("dy0", true); - options.Add("scale1", true); - options.Add("angle1", true); - options.Add("dy1", true); - options.Add("dx1", true); - - var results = this.Call("mosaic", options, sec, direction, xref, yref, xsec, ysec) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - dx0 = opts?["dx0"] is int out1 ? out1 : 0; - dy0 = opts?["dy0"] is int out2 ? out2 : 0; - scale1 = opts?["scale1"] is double out3 ? out3 : 0d; - angle1 = opts?["angle1"] is double out4 ? out4 : 0d; - dy1 = opts?["dy1"] is double out5 ? out5 : 0d; - dx1 = opts?["dx1"] is double out6 ? out6 : 0d; - - return finalResult; - } - - /// - /// First-order mosaic of two images. - /// - /// - /// - /// using Image @out = ref.Mosaic1(sec, direction, xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, hwindow: int, harea: int, search: bool, interpolate: GObject, mblend: int); - /// - /// - /// Secondary image. - /// Horizontal or vertical mosaic. - /// Position of first reference tie-point. - /// Position of first reference tie-point. - /// Position of first secondary tie-point. - /// Position of first secondary tie-point. - /// Position of second reference tie-point. - /// Position of second reference tie-point. - /// Position of second secondary tie-point. - /// Position of second secondary tie-point. - /// Half window size. - /// Half area size. - /// Search to improve tie-points. - /// Interpolate pixels with this. - /// Maximum blend size. - /// A new . - public Image Mosaic1(Image sec, Enums.Direction direction, int xr1, int yr1, int xs1, int ys1, int xr2, int yr2, int xs2, int ys2, int? hwindow = null, int? harea = null, bool? search = null, GObject interpolate = null, int? mblend = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(hwindow), hwindow); - options.AddIfPresent(nameof(harea), harea); - options.AddIfPresent(nameof(search), search); - options.AddIfPresent(nameof(interpolate), interpolate); - options.AddIfPresent(nameof(mblend), mblend); - - return this.Call("mosaic1", options, sec, direction, xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2) as Image; - } - - /// - /// Pick most-significant byte from an image. - /// - /// - /// - /// using Image @out = in.Msb(band: int); - /// - /// - /// Band to msb. - /// A new . - public Image Msb(int? band = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(band), band); - - return this.Call("msb", options) as Image; - } - - /// - /// Multiply two images. - /// - /// - /// - /// using Image @out = left.Multiply(right); - /// - /// - /// Right-hand image argument. - /// A new . - public Image Multiply(Image right) - { - return this.Call("multiply", right) as Image; - } - - /// - /// Load NIfTI volume. - /// - /// - /// - /// using Image @out = NetVips.Image.Niftiload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Niftiload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("niftiload", options, filename) as Image; - } - - /// - /// Load NIfTI volume. - /// - /// - /// - /// using Image @out = NetVips.Image.Niftiload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Niftiload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("niftiload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load NIfTI volumes. - /// - /// - /// - /// using Image @out = NetVips.Image.NiftiloadSource(source, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image NiftiloadSource(Source source, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("niftiload_source", options, source) as Image; - } - - /// - /// Load NIfTI volumes. - /// - /// - /// - /// using Image @out = NetVips.Image.NiftiloadStream(stream, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image NiftiloadStream(Stream stream, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = NiftiloadSource(source, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load NIfTI volumes. - /// - /// - /// - /// using Image @out = NetVips.Image.NiftiloadSource(source, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image NiftiloadSource(Source source, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("niftiload_source", options, source) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load NIfTI volumes. - /// - /// - /// - /// using Image @out = NetVips.Image.NiftiloadStream(stream, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image NiftiloadStream(Stream stream, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = NiftiloadSource(source, out flags, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Save image to nifti file. - /// - /// - /// - /// in.Niftisave(filename, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Filename to save to. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Niftisave(string filename, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("niftisave", options, filename); - } - - /// - /// Load an OpenEXR image. - /// - /// - /// - /// using Image @out = NetVips.Image.Openexrload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Openexrload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + {"angle", true}, + {"flip", true} + }; + + var results = this.Call("autorot", optionalOutput) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + angle = (Enums.Angle)opts?["angle"]; + flip = opts?["flip"] is bool out2 && out2; + + return finalResult; + } + + /// + /// Find image average. + /// + /// + /// + /// double @out = in.Avg(); + /// + /// + /// A double. + public double Avg() + { + return this.Call("avg") is double result ? result : 0d; + } + + /// + /// Boolean operation across image bands. + /// + /// + /// + /// using Image @out = in.Bandbool(boolean); + /// + /// + /// Boolean to perform. + /// A new . + public Image Bandbool(Enums.OperationBoolean boolean) + { + return this.Call("bandbool", boolean) as Image; + } + + /// + /// Fold up x axis into bands. + /// + /// + /// + /// using Image @out = in.Bandfold(factor: int); + /// + /// + /// Fold by this factor. + /// A new . + public Image Bandfold(int? factor = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(factor), factor); + + return this.Call("bandfold", options) as Image; + } + + /// + /// Append a constant band to an image. + /// + /// + /// + /// using Image @out = in.BandjoinConst(c); + /// + /// + /// Array of constants to add. + /// A new . + public Image BandjoinConst(double[] c) + { + return this.Call("bandjoin_const", c) as Image; + } + + /// + /// Band-wise average. + /// + /// + /// + /// using Image @out = in.Bandmean(); + /// + /// + /// A new . + public Image Bandmean() + { + return this.Call("bandmean") as Image; + } + + /// + /// Unfold image bands into x axis. + /// + /// + /// + /// using Image @out = in.Bandunfold(factor: int); + /// + /// + /// Unfold by this factor. + /// A new . + public Image Bandunfold(int? factor = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(factor), factor); + + return this.Call("bandunfold", options) as Image; + } + + /// + /// Make a black image. + /// + /// + /// + /// using Image @out = NetVips.Image.Black(width, height, bands: int); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Number of bands in image. + /// A new . + public static Image Black(int width, int height, int? bands = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(bands), bands); + + return Operation.Call("black", options, width, height) as Image; + } + + /// + /// Boolean operation on two images. + /// + /// + /// + /// using Image @out = left.Boolean(right, boolean); + /// + /// + /// Right-hand image argument. + /// Boolean to perform. + /// A new . + public Image Boolean(Image right, Enums.OperationBoolean boolean) + { + return this.Call("boolean", right, boolean) as Image; + } + + /// + /// Boolean operations against a constant. + /// + /// + /// + /// using Image @out = in.BooleanConst(boolean, c); + /// + /// + /// Boolean to perform. + /// Array of constants. + /// A new . + public Image BooleanConst(Enums.OperationBoolean boolean, double[] c) + { + return this.Call("boolean_const", boolean, c) as Image; + } + + /// + /// Build a look-up table. + /// + /// + /// + /// using Image @out = in.Buildlut(); + /// + /// + /// A new . + public Image Buildlut() + { + return this.Call("buildlut") as Image; + } + + /// + /// Byteswap an image. + /// + /// + /// + /// using Image @out = in.Byteswap(); + /// + /// + /// A new . + public Image Byteswap() + { + return this.Call("byteswap") as Image; + } + + /// + /// Cache an image. + /// + /// + /// + /// using Image @out = in.Cache(maxTiles: int, tileHeight: int, tileWidth: int); + /// + /// + /// Maximum number of tiles to cache. + /// Tile height in pixels. + /// Tile width in pixels. + /// A new . + public Image Cache(int? maxTiles = null, int? tileHeight = null, int? tileWidth = null) + { + var options = new VOption(); + + options.AddIfPresent("max_tiles", maxTiles); + options.AddIfPresent("tile_height", tileHeight); + options.AddIfPresent("tile_width", tileWidth); + + return this.Call("cache", options) as Image; + } + + /// + /// Canny edge detector. + /// + /// + /// + /// using Image @out = in.Canny(sigma: double, precision: Enums.Precision); + /// + /// + /// Sigma of Gaussian. + /// Convolve with this precision. + /// A new . + public Image Canny(double? sigma = null, Enums.Precision? precision = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(sigma), sigma); + options.AddIfPresent(nameof(precision), precision); + + return this.Call("canny", options) as Image; + } + + /// + /// Cast an image. + /// + /// + /// + /// using Image @out = in.Cast(format, shift: bool); + /// + /// + /// Format to cast to. + /// Shift integer values up and down. + /// A new . + public Image Cast(Enums.BandFormat format, bool? shift = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(shift), shift); + + return this.Call("cast", options, format) as Image; + } + + /// + /// Transform LCh to CMC. + /// + /// + /// + /// using Image @out = in.CMC2LCh(); + /// + /// + /// A new . + public Image CMC2LCh() + { + return this.Call("CMC2LCh") as Image; + } + + /// + /// Transform CMYK to XYZ. + /// + /// + /// + /// using Image @out = in.CMYK2XYZ(); + /// + /// + /// A new . + public Image CMYK2XYZ() + { + return this.Call("CMYK2XYZ") as Image; + } + + /// + /// Convert to a new colorspace. + /// + /// + /// + /// using Image @out = in.Colourspace(space, sourceSpace: Enums.Interpretation); + /// + /// + /// Destination color space. + /// Source color space. + /// A new . + public Image Colourspace(Enums.Interpretation space, Enums.Interpretation? sourceSpace = null) + { + var options = new VOption(); + + options.AddIfPresent("source_space", sourceSpace); + + return this.Call("colourspace", options, space) as Image; + } + + /// + /// Convolve with rotating mask. + /// + /// + /// + /// using Image @out = in.Compass(mask, times: int, angle: Enums.Angle45, combine: Enums.Combine, precision: Enums.Precision, layers: int, cluster: int); + /// + /// + /// Input matrix image. + /// Rotate and convolve this many times. + /// Rotate mask by this much between convolutions. + /// Combine convolution results like this. + /// Convolve with this precision. + /// Use this many layers in approximation. + /// Cluster lines closer than this in approximation. + /// A new . + public Image Compass(Image mask, int? times = null, Enums.Angle45? angle = null, Enums.Combine? combine = null, Enums.Precision? precision = null, int? layers = null, int? cluster = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(times), times); + options.AddIfPresent(nameof(angle), angle); + options.AddIfPresent(nameof(combine), combine); + options.AddIfPresent(nameof(precision), precision); + options.AddIfPresent(nameof(layers), layers); + options.AddIfPresent(nameof(cluster), cluster); + + return this.Call("compass", options, mask) as Image; + } + + /// + /// Perform a complex operation on an image. + /// + /// + /// + /// using Image @out = in.Complex(cmplx); + /// + /// + /// Complex to perform. + /// A new . + public Image Complex(Enums.OperationComplex cmplx) + { + return this.Call("complex", cmplx) as Image; + } + + /// + /// Complex binary operations on two images. + /// + /// + /// + /// using Image @out = left.Complex2(right, cmplx); + /// + /// + /// Right-hand image argument. + /// Binary complex operation to perform. + /// A new . + public Image Complex2(Image right, Enums.OperationComplex2 cmplx) + { + return this.Call("complex2", right, cmplx) as Image; + } + + /// + /// Form a complex image from two real images. + /// + /// + /// + /// using Image @out = left.Complexform(right); + /// + /// + /// Right-hand image argument. + /// A new . + public Image Complexform(Image right) + { + return this.Call("complexform", right) as Image; + } + + /// + /// Get a component from a complex image. + /// + /// + /// + /// using Image @out = in.Complexget(get); + /// + /// + /// Complex to perform. + /// A new . + public Image Complexget(Enums.OperationComplexget get) + { + return this.Call("complexget", get) as Image; + } + + /// + /// Blend a pair of images with a blend mode. + /// + /// + /// + /// using Image @out = base.Composite2(overlay, mode, x: int, y: int, compositingSpace: Enums.Interpretation, premultiplied: bool); + /// + /// + /// Overlay image. + /// VipsBlendMode to join with. + /// x position of overlay. + /// y position of overlay. + /// Composite images in this colour space. + /// Images have premultiplied alpha. + /// A new . + public Image Composite2(Image overlay, Enums.BlendMode mode, int? x = null, int? y = null, Enums.Interpretation? compositingSpace = null, bool? premultiplied = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(x), x); + options.AddIfPresent(nameof(y), y); + options.AddIfPresent("compositing_space", compositingSpace); + options.AddIfPresent(nameof(premultiplied), premultiplied); + + return this.Call("composite2", options, overlay, mode) as Image; + } + + /// + /// Convolution operation. + /// + /// + /// + /// using Image @out = in.Conv(mask, precision: Enums.Precision, layers: int, cluster: int); + /// + /// + /// Input matrix image. + /// Convolve with this precision. + /// Use this many layers in approximation. + /// Cluster lines closer than this in approximation. + /// A new . + public Image Conv(Image mask, Enums.Precision? precision = null, int? layers = null, int? cluster = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(precision), precision); + options.AddIfPresent(nameof(layers), layers); + options.AddIfPresent(nameof(cluster), cluster); + + return this.Call("conv", options, mask) as Image; + } + + /// + /// Approximate integer convolution. + /// + /// + /// + /// using Image @out = in.Conva(mask, layers: int, cluster: int); + /// + /// + /// Input matrix image. + /// Use this many layers in approximation. + /// Cluster lines closer than this in approximation. + /// A new . + public Image Conva(Image mask, int? layers = null, int? cluster = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(layers), layers); + options.AddIfPresent(nameof(cluster), cluster); + + return this.Call("conva", options, mask) as Image; + } + + /// + /// Approximate separable integer convolution. + /// + /// + /// + /// using Image @out = in.Convasep(mask, layers: int); + /// + /// + /// Input matrix image. + /// Use this many layers in approximation. + /// A new . + public Image Convasep(Image mask, int? layers = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(layers), layers); + + return this.Call("convasep", options, mask) as Image; + } + + /// + /// Float convolution operation. + /// + /// + /// + /// using Image @out = in.Convf(mask); + /// + /// + /// Input matrix image. + /// A new . + public Image Convf(Image mask) + { + return this.Call("convf", mask) as Image; + } + + /// + /// Int convolution operation. + /// + /// + /// + /// using Image @out = in.Convi(mask); + /// + /// + /// Input matrix image. + /// A new . + public Image Convi(Image mask) + { + return this.Call("convi", mask) as Image; + } + + /// + /// Separable convolution operation. + /// + /// + /// + /// using Image @out = in.Convsep(mask, precision: Enums.Precision, layers: int, cluster: int); + /// + /// + /// Input matrix image. + /// Convolve with this precision. + /// Use this many layers in approximation. + /// Cluster lines closer than this in approximation. + /// A new . + public Image Convsep(Image mask, Enums.Precision? precision = null, int? layers = null, int? cluster = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(precision), precision); + options.AddIfPresent(nameof(layers), layers); + options.AddIfPresent(nameof(cluster), cluster); + + return this.Call("convsep", options, mask) as Image; + } + + /// + /// Copy an image. + /// + /// + /// + /// using Image @out = in.Copy(width: int, height: int, bands: int, format: Enums.BandFormat, coding: Enums.Coding, interpretation: Enums.Interpretation, xres: double, yres: double, xoffset: int, yoffset: int); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Number of bands in image. + /// Pixel format in image. + /// Pixel coding. + /// Pixel interpretation. + /// Horizontal resolution in pixels/mm. + /// Vertical resolution in pixels/mm. + /// Horizontal offset of origin. + /// Vertical offset of origin. + /// A new . + public Image Copy(int? width = null, int? height = null, int? bands = null, Enums.BandFormat? format = null, Enums.Coding? coding = null, Enums.Interpretation? interpretation = null, double? xres = null, double? yres = null, int? xoffset = null, int? yoffset = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(width), width); + options.AddIfPresent(nameof(height), height); + options.AddIfPresent(nameof(bands), bands); + options.AddIfPresent(nameof(format), format); + options.AddIfPresent(nameof(coding), coding); + options.AddIfPresent(nameof(interpretation), interpretation); + options.AddIfPresent(nameof(xres), xres); + options.AddIfPresent(nameof(yres), yres); + options.AddIfPresent(nameof(xoffset), xoffset); + options.AddIfPresent(nameof(yoffset), yoffset); + + return this.Call("copy", options) as Image; + } + + /// + /// Count lines in an image. + /// + /// + /// + /// double nolines = in.Countlines(direction); + /// + /// + /// Countlines left-right or up-down. + /// A double. + public double Countlines(Enums.Direction direction) + { + return this.Call("countlines", direction) is double result ? result : 0d; + } + + /// + /// Load csv. + /// + /// + /// + /// using Image @out = NetVips.Image.Csvload(filename, skip: int, lines: int, whitespace: string, separator: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Skip this many lines at the start of the file. + /// Read this many lines from the file. + /// Set of whitespace characters. + /// Set of separator characters. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Csvload(string filename, int? skip = null, int? lines = null, string whitespace = null, string separator = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(skip), skip); + options.AddIfPresent(nameof(lines), lines); + options.AddIfPresent(nameof(whitespace), whitespace); + options.AddIfPresent(nameof(separator), separator); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("csvload", options, filename) as Image; + } + + /// + /// Load csv. + /// + /// + /// + /// using Image @out = NetVips.Image.Csvload(filename, out var flags, skip: int, lines: int, whitespace: string, separator: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// Skip this many lines at the start of the file. + /// Read this many lines from the file. + /// Set of whitespace characters. + /// Set of separator characters. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Csvload(string filename, out Enums.ForeignFlags flags, int? skip = null, int? lines = null, string whitespace = null, string separator = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(skip), skip); + options.AddIfPresent(nameof(lines), lines); + options.AddIfPresent(nameof(whitespace), whitespace); + options.AddIfPresent(nameof(separator), separator); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("csvload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load csv. + /// + /// + /// + /// using Image @out = NetVips.Image.CsvloadSource(source, skip: int, lines: int, whitespace: string, separator: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Skip this many lines at the start of the file. + /// Read this many lines from the file. + /// Set of whitespace characters. + /// Set of separator characters. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image CsvloadSource(Source source, int? skip = null, int? lines = null, string whitespace = null, string separator = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(skip), skip); + options.AddIfPresent(nameof(lines), lines); + options.AddIfPresent(nameof(whitespace), whitespace); + options.AddIfPresent(nameof(separator), separator); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("csvload_source", options, source) as Image; + } + + /// + /// Load csv. + /// + /// + /// + /// using Image @out = NetVips.Image.CsvloadStream(stream, skip: int, lines: int, whitespace: string, separator: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Skip this many lines at the start of the file. + /// Read this many lines from the file. + /// Set of whitespace characters. + /// Set of separator characters. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image CsvloadStream(Stream stream, int? skip = null, int? lines = null, string whitespace = null, string separator = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = CsvloadSource(source, skip, lines, whitespace, separator, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load csv. + /// + /// + /// + /// using Image @out = NetVips.Image.CsvloadSource(source, out var flags, skip: int, lines: int, whitespace: string, separator: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Flags for this file. + /// Skip this many lines at the start of the file. + /// Read this many lines from the file. + /// Set of whitespace characters. + /// Set of separator characters. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image CsvloadSource(Source source, out Enums.ForeignFlags flags, int? skip = null, int? lines = null, string whitespace = null, string separator = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(skip), skip); + options.AddIfPresent(nameof(lines), lines); + options.AddIfPresent(nameof(whitespace), whitespace); + options.AddIfPresent(nameof(separator), separator); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("csvload_source", options, source) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load csv. + /// + /// + /// + /// using Image @out = NetVips.Image.CsvloadStream(stream, out var flags, skip: int, lines: int, whitespace: string, separator: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Flags for this file. + /// Skip this many lines at the start of the file. + /// Read this many lines from the file. + /// Set of whitespace characters. + /// Set of separator characters. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image CsvloadStream(Stream stream, out Enums.ForeignFlags flags, int? skip = null, int? lines = null, string whitespace = null, string separator = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = CsvloadSource(source, out flags, skip, lines, whitespace, separator, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Save image to csv. + /// + /// + /// + /// in.Csvsave(filename, separator: string, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Filename to save to. + /// Separator characters. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Csvsave(string filename, string separator = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(separator), separator); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("csvsave", options, filename); + } + + /// + /// Save image to csv. + /// + /// + /// + /// in.CsvsaveTarget(target, separator: string, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Target to save to. + /// Separator characters. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void CsvsaveTarget(Target target, string separator = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(separator), separator); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("csvsave_target", options, target); + } + + /// + /// Save image to csv. + /// + /// + /// + /// in.CsvsaveStream(stream, separator: string, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Stream to save to. + /// Separator characters. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void CsvsaveStream(Stream stream, string separator = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + using var target = TargetStream.NewFromStream(stream); + CsvsaveTarget(target, separator, keep, background, pageHeight, profile); + } + + /// + /// Calculate dE00. + /// + /// + /// + /// using Image @out = left.DE00(right); + /// + /// + /// Right-hand input image. + /// A new . + public Image DE00(Image right) + { + return this.Call("dE00", right) as Image; + } + + /// + /// Calculate dE76. + /// + /// + /// + /// using Image @out = left.DE76(right); + /// + /// + /// Right-hand input image. + /// A new . + public Image DE76(Image right) + { + return this.Call("dE76", right) as Image; + } + + /// + /// Calculate dECMC. + /// + /// + /// + /// using Image @out = left.DECMC(right); + /// + /// + /// Right-hand input image. + /// A new . + public Image DECMC(Image right) + { + return this.Call("dECMC", right) as Image; + } + + /// + /// Find image standard deviation. + /// + /// + /// + /// double @out = in.Deviate(); + /// + /// + /// A double. + public double Deviate() + { + return this.Call("deviate") is double result ? result : 0d; + } + + /// + /// Divide two images. + /// + /// + /// + /// using Image @out = left.Divide(right); + /// + /// + /// Right-hand image argument. + /// A new . + public Image Divide(Image right) + { + return this.Call("divide", right) as Image; + } + + /// + /// Save image to deepzoom file. + /// + /// + /// + /// in.Dzsave(filename, imagename: string, layout: Enums.ForeignDzLayout, suffix: string, overlap: int, tileSize: int, centre: bool, depth: Enums.ForeignDzDepth, angle: Enums.Angle, container: Enums.ForeignDzContainer, compression: int, regionShrink: Enums.RegionShrink, skipBlanks: int, id: string, q: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Filename to save to. + /// Image name. + /// Directory layout. + /// Filename suffix for tiles. + /// Tile overlap in pixels. + /// Tile size in pixels. + /// Center image in tile. + /// Pyramid depth. + /// Rotate image during save. + /// Pyramid container type. + /// ZIP deflate compression level. + /// Method to shrink regions. + /// Skip tiles which are nearly equal to the background. + /// Resource ID. + /// Q factor. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Dzsave(string filename, string imagename = null, Enums.ForeignDzLayout? layout = null, string suffix = null, int? overlap = null, int? tileSize = null, bool? centre = null, Enums.ForeignDzDepth? depth = null, Enums.Angle? angle = null, Enums.ForeignDzContainer? container = null, int? compression = null, Enums.RegionShrink? regionShrink = null, int? skipBlanks = null, string id = null, int? q = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(NetVips.AtLeastLibvips(8, 15) ? nameof(imagename) : "basename", imagename); + options.AddIfPresent(nameof(layout), layout); + options.AddIfPresent(nameof(suffix), suffix); + options.AddIfPresent(nameof(overlap), overlap); + options.AddIfPresent("tile_size", tileSize); + options.AddIfPresent(nameof(centre), centre); + options.AddIfPresent(nameof(depth), depth); + options.AddIfPresent(nameof(angle), angle); + options.AddIfPresent(nameof(container), container); + options.AddIfPresent(nameof(compression), compression); + options.AddIfPresent("region_shrink", regionShrink); + options.AddIfPresent("skip_blanks", skipBlanks); + options.AddIfPresent(nameof(id), id); + options.AddIfPresent("Q", q); + options.AddForeignKeep(keep, true); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("dzsave", options, filename); + } + + /// + /// Save image to dz buffer. + /// + /// + /// + /// byte[] buffer = in.DzsaveBuffer(imagename: string, layout: Enums.ForeignDzLayout, suffix: string, overlap: int, tileSize: int, centre: bool, depth: Enums.ForeignDzDepth, angle: Enums.Angle, container: Enums.ForeignDzContainer, compression: int, regionShrink: Enums.RegionShrink, skipBlanks: int, id: string, q: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Image name. + /// Directory layout. + /// Filename suffix for tiles. + /// Tile overlap in pixels. + /// Tile size in pixels. + /// Center image in tile. + /// Pyramid depth. + /// Rotate image during save. + /// Pyramid container type. + /// ZIP deflate compression level. + /// Method to shrink regions. + /// Skip tiles which are nearly equal to the background. + /// Resource ID. + /// Q factor. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + /// An array of bytes. + public byte[] DzsaveBuffer(string imagename = null, Enums.ForeignDzLayout? layout = null, string suffix = null, int? overlap = null, int? tileSize = null, bool? centre = null, Enums.ForeignDzDepth? depth = null, Enums.Angle? angle = null, Enums.ForeignDzContainer? container = null, int? compression = null, Enums.RegionShrink? regionShrink = null, int? skipBlanks = null, string id = null, int? q = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(NetVips.AtLeastLibvips(8, 15) ? nameof(imagename) : "basename", imagename); + options.AddIfPresent(nameof(layout), layout); + options.AddIfPresent(nameof(suffix), suffix); + options.AddIfPresent(nameof(overlap), overlap); + options.AddIfPresent("tile_size", tileSize); + options.AddIfPresent(nameof(centre), centre); + options.AddIfPresent(nameof(depth), depth); + options.AddIfPresent(nameof(angle), angle); + options.AddIfPresent(nameof(container), container); + options.AddIfPresent(nameof(compression), compression); + options.AddIfPresent("region_shrink", regionShrink); + options.AddIfPresent("skip_blanks", skipBlanks); + options.AddIfPresent(nameof(id), id); + options.AddIfPresent("Q", q); + options.AddForeignKeep(keep, true); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + return this.Call("dzsave_buffer", options) as byte[]; + } + + /// + /// Save image to deepzoom target. + /// + /// + /// + /// in.DzsaveTarget(target, imagename: string, layout: Enums.ForeignDzLayout, suffix: string, overlap: int, tileSize: int, centre: bool, depth: Enums.ForeignDzDepth, angle: Enums.Angle, container: Enums.ForeignDzContainer, compression: int, regionShrink: Enums.RegionShrink, skipBlanks: int, id: string, q: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Target to save to. + /// Image name. + /// Directory layout. + /// Filename suffix for tiles. + /// Tile overlap in pixels. + /// Tile size in pixels. + /// Center image in tile. + /// Pyramid depth. + /// Rotate image during save. + /// Pyramid container type. + /// ZIP deflate compression level. + /// Method to shrink regions. + /// Skip tiles which are nearly equal to the background. + /// Resource ID. + /// Q factor. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void DzsaveTarget(Target target, string imagename = null, Enums.ForeignDzLayout? layout = null, string suffix = null, int? overlap = null, int? tileSize = null, bool? centre = null, Enums.ForeignDzDepth? depth = null, Enums.Angle? angle = null, Enums.ForeignDzContainer? container = null, int? compression = null, Enums.RegionShrink? regionShrink = null, int? skipBlanks = null, string id = null, int? q = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(NetVips.AtLeastLibvips(8, 15) ? nameof(imagename) : "basename", imagename); + options.AddIfPresent(nameof(layout), layout); + options.AddIfPresent(nameof(suffix), suffix); + options.AddIfPresent(nameof(overlap), overlap); + options.AddIfPresent("tile_size", tileSize); + options.AddIfPresent(nameof(centre), centre); + options.AddIfPresent(nameof(depth), depth); + options.AddIfPresent(nameof(angle), angle); + options.AddIfPresent(nameof(container), container); + options.AddIfPresent(nameof(compression), compression); + options.AddIfPresent("region_shrink", regionShrink); + options.AddIfPresent("skip_blanks", skipBlanks); + options.AddIfPresent(nameof(id), id); + options.AddIfPresent("Q", q); + options.AddForeignKeep(keep, true); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("dzsave_target", options, target); + } + + /// + /// Save image to deepzoom stream. + /// + /// + /// + /// in.DzsaveStream(stream, imagename: string, layout: Enums.ForeignDzLayout, suffix: string, overlap: int, tileSize: int, centre: bool, depth: Enums.ForeignDzDepth, angle: Enums.Angle, container: Enums.ForeignDzContainer, compression: int, regionShrink: Enums.RegionShrink, skipBlanks: int, id: string, q: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Stream to save to. + /// Image name. + /// Directory layout. + /// Filename suffix for tiles. + /// Tile overlap in pixels. + /// Tile size in pixels. + /// Center image in tile. + /// Pyramid depth. + /// Rotate image during save. + /// Pyramid container type. + /// ZIP deflate compression level. + /// Method to shrink regions. + /// Skip tiles which are nearly equal to the background. + /// Resource ID. + /// Q factor. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void DzsaveStream(Stream stream, string imagename = null, Enums.ForeignDzLayout? layout = null, string suffix = null, int? overlap = null, int? tileSize = null, bool? centre = null, Enums.ForeignDzDepth? depth = null, Enums.Angle? angle = null, Enums.ForeignDzContainer? container = null, int? compression = null, Enums.RegionShrink? regionShrink = null, int? skipBlanks = null, string id = null, int? q = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + using var target = TargetStream.NewFromStream(stream); + DzsaveTarget(target, imagename, layout, suffix, overlap, tileSize, centre, depth, angle, container, compression, regionShrink, skipBlanks, id, q, keep, background, pageHeight, profile); + } + + /// + /// Embed an image in a larger image. + /// + /// + /// + /// using Image @out = in.Embed(x, y, width, height, extend: Enums.Extend, background: double[]); + /// + /// + /// Left edge of input in output. + /// Top edge of input in output. + /// Image width in pixels. + /// Image height in pixels. + /// How to generate the extra pixels. + /// Color for background pixels. + /// A new . + public Image Embed(int x, int y, int width, int height, Enums.Extend? extend = null, double[] background = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(extend), extend); + options.AddIfPresent(nameof(background), background); + + return this.Call("embed", options, x, y, width, height) as Image; + } + + /// + /// Extract an area from an image. + /// + /// + /// + /// using Image @out = input.ExtractArea(left, top, width, height); + /// + /// + /// Left edge of extract area. + /// Top edge of extract area. + /// Width of extract area. + /// Height of extract area. + /// A new . + public Image ExtractArea(int left, int top, int width, int height) + { + return this.Call("extract_area", left, top, width, height) as Image; + } + + /// + /// Extract band from an image. + /// + /// + /// + /// using Image @out = in.ExtractBand(band, n: int); + /// + /// + /// Band to extract. + /// Number of bands to extract. + /// A new . + public Image ExtractBand(int band, int? n = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(n), n); + + return this.Call("extract_band", options, band) as Image; + } + + /// + /// Make an image showing the eye's spatial response. + /// + /// + /// + /// using Image @out = NetVips.Image.Eye(width, height, uchar: bool, factor: double); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Output an unsigned char image. + /// Maximum spatial frequency. + /// A new . + public static Image Eye(int width, int height, bool? uchar = null, double? factor = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(uchar), uchar); + options.AddIfPresent(nameof(factor), factor); + + return Operation.Call("eye", options, width, height) as Image; + } + + /// + /// False-color an image. + /// + /// + /// + /// using Image @out = in.Falsecolour(); + /// + /// + /// A new . + public Image Falsecolour() + { + return this.Call("falsecolour") as Image; + } + + /// + /// Fast correlation. + /// + /// + /// + /// using Image @out = in.Fastcor(@ref); + /// + /// + /// Input reference image. + /// A new . + public Image Fastcor(Image @ref) + { + return this.Call("fastcor", @ref) as Image; + } + + /// + /// Fill image zeros with nearest non-zero pixel. + /// + /// + /// + /// using Image @out = in.FillNearest(); + /// + /// + /// A new . + public Image FillNearest() + { + return this.Call("fill_nearest") as Image; + } + + /// + /// Fill image zeros with nearest non-zero pixel. + /// + /// + /// + /// using Image @out = in.FillNearest(out var distance); + /// + /// + /// Distance to nearest non-zero pixel. + /// A new . + public Image FillNearest(out Image distance) + { + var optionalOutput = new VOption { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("openexrload", options, filename) as Image; - } - - /// - /// Load an OpenEXR image. - /// - /// - /// - /// using Image @out = NetVips.Image.Openexrload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Openexrload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("openexrload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load file with OpenSlide. - /// - /// - /// - /// using Image @out = NetVips.Image.Openslideload(filename, level: int, autocrop: bool, associated: string, attachAssociated: bool, rgb: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Load this level from the file. - /// Crop to image bounds. - /// Load this associated image. - /// Attach all associated images. - /// Output RGB (not RGBA). - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Openslideload(string filename, int? level = null, bool? autocrop = null, string associated = null, bool? attachAssociated = null, bool? rgb = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(level), level); - options.AddIfPresent(nameof(autocrop), autocrop); - options.AddIfPresent(nameof(associated), associated); - options.AddIfPresent("attach_associated", attachAssociated); - options.AddIfPresent(nameof(rgb), rgb); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("openslideload", options, filename) as Image; - } - - /// - /// Load file with OpenSlide. - /// - /// - /// - /// using Image @out = NetVips.Image.Openslideload(filename, out var flags, level: int, autocrop: bool, associated: string, attachAssociated: bool, rgb: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// Load this level from the file. - /// Crop to image bounds. - /// Load this associated image. - /// Attach all associated images. - /// Output RGB (not RGBA). - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Openslideload(string filename, out Enums.ForeignFlags flags, int? level = null, bool? autocrop = null, string associated = null, bool? attachAssociated = null, bool? rgb = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(level), level); - options.AddIfPresent(nameof(autocrop), autocrop); - options.AddIfPresent(nameof(associated), associated); - options.AddIfPresent("attach_associated", attachAssociated); - options.AddIfPresent(nameof(rgb), rgb); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("openslideload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load source with OpenSlide. - /// - /// - /// - /// using Image @out = NetVips.Image.OpenslideloadSource(source, level: int, autocrop: bool, associated: string, attachAssociated: bool, rgb: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Load this level from the file. - /// Crop to image bounds. - /// Load this associated image. - /// Attach all associated images. - /// Output RGB (not RGBA). - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image OpenslideloadSource(Source source, int? level = null, bool? autocrop = null, string associated = null, bool? attachAssociated = null, bool? rgb = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(level), level); - options.AddIfPresent(nameof(autocrop), autocrop); - options.AddIfPresent(nameof(associated), associated); - options.AddIfPresent("attach_associated", attachAssociated); - options.AddIfPresent(nameof(rgb), rgb); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("openslideload_source", options, source) as Image; - } - - /// - /// Load stream with OpenSlide. - /// - /// - /// - /// using Image @out = NetVips.Image.OpenslideloadStream(stream, level: int, autocrop: bool, associated: string, attachAssociated: bool, rgb: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Load this level from the file. - /// Crop to image bounds. - /// Load this associated image. - /// Attach all associated images. - /// Output RGB (not RGBA). - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image OpenslideloadStream(Stream stream, int? level = null, bool? autocrop = null, string associated = null, bool? attachAssociated = null, bool? rgb = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = OpenslideloadSource(source, level, autocrop, associated, attachAssociated, rgb, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load source with OpenSlide. - /// - /// - /// - /// using Image @out = NetVips.Image.OpenslideloadSource(source, out var flags, level: int, autocrop: bool, associated: string, attachAssociated: bool, rgb: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Flags for this file. - /// Load this level from the file. - /// Crop to image bounds. - /// Load this associated image. - /// Attach all associated images. - /// Output RGB (not RGBA). - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image OpenslideloadSource(Source source, out Enums.ForeignFlags flags, int? level = null, bool? autocrop = null, string associated = null, bool? attachAssociated = null, bool? rgb = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(level), level); - options.AddIfPresent(nameof(autocrop), autocrop); - options.AddIfPresent(nameof(associated), associated); - options.AddIfPresent("attach_associated", attachAssociated); - options.AddIfPresent(nameof(rgb), rgb); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("openslideload_source", options, source) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load stream with OpenSlide. - /// - /// - /// - /// using Image @out = NetVips.Image.OpenslideloadStream(stream, out var flags, level: int, autocrop: bool, associated: string, attachAssociated: bool, rgb: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Flags for this file. - /// Load this level from the file. - /// Crop to image bounds. - /// Load this associated image. - /// Attach all associated images. - /// Output RGB (not RGBA). - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image OpenslideloadStream(Stream stream, out Enums.ForeignFlags flags, int? level = null, bool? autocrop = null, string associated = null, bool? attachAssociated = null, bool? rgb = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = OpenslideloadSource(source, out flags, level, autocrop, associated, attachAssociated, rgb, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load PDF from file. - /// - /// - /// - /// using Image @out = NetVips.Image.Pdfload(filename, page: int, n: int, dpi: double, scale: double, background: double[], password: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// First page to load. - /// Number of pages to load, -1 for all. - /// DPI to render at. - /// Factor to scale by. - /// Background colour. - /// Password to decrypt with. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Pdfload(string filename, int? page = null, int? n = null, double? dpi = null, double? scale = null, double[] background = null, string password = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(dpi), dpi); - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent(nameof(password), password); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("pdfload", options, filename) as Image; - } - - /// - /// Load PDF from file. - /// - /// - /// - /// using Image @out = NetVips.Image.Pdfload(filename, out var flags, page: int, n: int, dpi: double, scale: double, background: double[], password: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// First page to load. - /// Number of pages to load, -1 for all. - /// DPI to render at. - /// Factor to scale by. - /// Background colour. - /// Password to decrypt with. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Pdfload(string filename, out Enums.ForeignFlags flags, int? page = null, int? n = null, double? dpi = null, double? scale = null, double[] background = null, string password = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(dpi), dpi); - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent(nameof(password), password); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("pdfload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load PDF from buffer. - /// - /// - /// - /// using Image @out = NetVips.Image.PdfloadBuffer(buffer, page: int, n: int, dpi: double, scale: double, background: double[], password: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// First page to load. - /// Number of pages to load, -1 for all. - /// DPI to render at. - /// Factor to scale by. - /// Background colour. - /// Password to decrypt with. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image PdfloadBuffer(byte[] buffer, int? page = null, int? n = null, double? dpi = null, double? scale = null, double[] background = null, string password = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(dpi), dpi); - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent(nameof(password), password); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("pdfload_buffer", options, buffer) as Image; - } - - /// - /// Load PDF from buffer. - /// - /// - /// - /// using Image @out = NetVips.Image.PdfloadBuffer(buffer, out var flags, page: int, n: int, dpi: double, scale: double, background: double[], password: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Flags for this file. - /// First page to load. - /// Number of pages to load, -1 for all. - /// DPI to render at. - /// Factor to scale by. - /// Background colour. - /// Password to decrypt with. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image PdfloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, int? page = null, int? n = null, double? dpi = null, double? scale = null, double[] background = null, string password = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(dpi), dpi); - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent(nameof(password), password); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("pdfload_buffer", options, buffer) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load PDF from source. - /// - /// - /// - /// using Image @out = NetVips.Image.PdfloadSource(source, page: int, n: int, dpi: double, scale: double, background: double[], password: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// First page to load. - /// Number of pages to load, -1 for all. - /// DPI to render at. - /// Factor to scale by. - /// Background colour. - /// Password to decrypt with. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image PdfloadSource(Source source, int? page = null, int? n = null, double? dpi = null, double? scale = null, double[] background = null, string password = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(dpi), dpi); - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent(nameof(password), password); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("pdfload_source", options, source) as Image; - } - - /// - /// Load PDF from stream. - /// - /// - /// - /// using Image @out = NetVips.Image.PdfloadStream(stream, page: int, n: int, dpi: double, scale: double, background: double[], password: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// First page to load. - /// Number of pages to load, -1 for all. - /// DPI to render at. - /// Factor to scale by. - /// Background colour. - /// Password to decrypt with. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image PdfloadStream(Stream stream, int? page = null, int? n = null, double? dpi = null, double? scale = null, double[] background = null, string password = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = PdfloadSource(source, page, n, dpi, scale, background, password, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load PDF from source. - /// - /// - /// - /// using Image @out = NetVips.Image.PdfloadSource(source, out var flags, page: int, n: int, dpi: double, scale: double, background: double[], password: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Flags for this file. - /// First page to load. - /// Number of pages to load, -1 for all. - /// DPI to render at. - /// Factor to scale by. - /// Background colour. - /// Password to decrypt with. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image PdfloadSource(Source source, out Enums.ForeignFlags flags, int? page = null, int? n = null, double? dpi = null, double? scale = null, double[] background = null, string password = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(dpi), dpi); - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent(nameof(password), password); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("pdfload_source", options, source) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load PDF from stream. - /// - /// - /// - /// using Image @out = NetVips.Image.PdfloadStream(stream, out var flags, page: int, n: int, dpi: double, scale: double, background: double[], password: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Flags for this file. - /// First page to load. - /// Number of pages to load, -1 for all. - /// DPI to render at. - /// Factor to scale by. - /// Background colour. - /// Password to decrypt with. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image PdfloadStream(Stream stream, out Enums.ForeignFlags flags, int? page = null, int? n = null, double? dpi = null, double? scale = null, double[] background = null, string password = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = PdfloadSource(source, out flags, page, n, dpi, scale, background, password, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Find threshold for percent of pixels. - /// - /// - /// - /// int threshold = in.Percent(percent); - /// - /// - /// Percent of pixels. - /// A int. - public int Percent(double percent) - { - return this.Call("percent", percent) is int result ? result : 0; - } - - /// - /// Make a perlin noise image. - /// - /// - /// - /// using Image @out = NetVips.Image.Perlin(width, height, cellSize: int, uchar: bool, seed: int); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Size of Perlin cells. - /// Output an unsigned char image. - /// Random number seed. - /// A new . - public static Image Perlin(int width, int height, int? cellSize = null, bool? uchar = null, int? seed = null) - { - var options = new VOption(); - - options.AddIfPresent("cell_size", cellSize); - options.AddIfPresent(nameof(uchar), uchar); - options.AddIfPresent(nameof(seed), seed); - - return Operation.Call("perlin", options, width, height) as Image; - } - - /// - /// Calculate phase correlation. - /// - /// - /// - /// using Image @out = in.Phasecor(in2); - /// - /// - /// Second input image. - /// A new . - public Image Phasecor(Image in2) - { - return this.Call("phasecor", in2) as Image; - } - - /// - /// Load png from file. - /// - /// - /// - /// using Image @out = NetVips.Image.Pngload(filename, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Pngload(string filename, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("pngload", options, filename) as Image; - } - - /// - /// Load png from file. - /// - /// - /// - /// using Image @out = NetVips.Image.Pngload(filename, out var flags, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Pngload(string filename, out Enums.ForeignFlags flags, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("pngload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load png from buffer. - /// - /// - /// - /// using Image @out = NetVips.Image.PngloadBuffer(buffer, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image PngloadBuffer(byte[] buffer, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("pngload_buffer", options, buffer) as Image; - } - - /// - /// Load png from buffer. - /// - /// - /// - /// using Image @out = NetVips.Image.PngloadBuffer(buffer, out var flags, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Flags for this file. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image PngloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("pngload_buffer", options, buffer) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load png from source. - /// - /// - /// - /// using Image @out = NetVips.Image.PngloadSource(source, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image PngloadSource(Source source, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("pngload_source", options, source) as Image; - } - - /// - /// Load png from stream. - /// - /// - /// - /// using Image @out = NetVips.Image.PngloadStream(stream, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image PngloadStream(Stream stream, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = PngloadSource(source, unlimited, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load png from source. - /// - /// - /// - /// using Image @out = NetVips.Image.PngloadSource(source, out var flags, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Flags for this file. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image PngloadSource(Source source, out Enums.ForeignFlags flags, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("pngload_source", options, source) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load png from stream. - /// - /// - /// - /// using Image @out = NetVips.Image.PngloadStream(stream, out var flags, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Flags for this file. - /// Remove all denial of service limits. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image PngloadStream(Stream stream, out Enums.ForeignFlags flags, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = PngloadSource(source, out flags, unlimited, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Save image to file as PNG. - /// - /// - /// - /// in.Pngsave(filename, compression: int, interlace: bool, filter: Enums.ForeignPngFilter, palette: bool, q: int, dither: double, bitdepth: int, effort: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Filename to save to. - /// Compression factor. - /// Interlace image. - /// libspng row filter flag(s). - /// Quantise to 8bpp palette. - /// Quantisation quality. - /// Amount of dithering. - /// Write as a 1, 2, 4, 8 or 16 bit image. - /// Quantisation CPU effort. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Pngsave(string filename, int? compression = null, bool? interlace = null, Enums.ForeignPngFilter? filter = null, bool? palette = null, int? q = null, double? dither = null, int? bitdepth = null, int? effort = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(compression), compression); - options.AddIfPresent(nameof(interlace), interlace); - options.AddIfPresent(nameof(filter), filter); - options.AddIfPresent(nameof(palette), palette); - options.AddIfPresent("Q", q); - options.AddIfPresent(nameof(dither), dither); - options.AddIfPresent(nameof(bitdepth), bitdepth); - options.AddIfPresent(nameof(effort), effort); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("pngsave", options, filename); - } - - /// - /// Save image to buffer as PNG. - /// - /// - /// - /// byte[] buffer = in.PngsaveBuffer(compression: int, interlace: bool, filter: Enums.ForeignPngFilter, palette: bool, q: int, dither: double, bitdepth: int, effort: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Compression factor. - /// Interlace image. - /// libspng row filter flag(s). - /// Quantise to 8bpp palette. - /// Quantisation quality. - /// Amount of dithering. - /// Write as a 1, 2, 4, 8 or 16 bit image. - /// Quantisation CPU effort. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - /// An array of bytes. - public byte[] PngsaveBuffer(int? compression = null, bool? interlace = null, Enums.ForeignPngFilter? filter = null, bool? palette = null, int? q = null, double? dither = null, int? bitdepth = null, int? effort = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(compression), compression); - options.AddIfPresent(nameof(interlace), interlace); - options.AddIfPresent(nameof(filter), filter); - options.AddIfPresent(nameof(palette), palette); - options.AddIfPresent("Q", q); - options.AddIfPresent(nameof(dither), dither); - options.AddIfPresent(nameof(bitdepth), bitdepth); - options.AddIfPresent(nameof(effort), effort); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - return this.Call("pngsave_buffer", options) as byte[]; - } - - /// - /// Save image to target as PNG. - /// - /// - /// - /// in.PngsaveTarget(target, compression: int, interlace: bool, filter: Enums.ForeignPngFilter, palette: bool, q: int, dither: double, bitdepth: int, effort: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Target to save to. - /// Compression factor. - /// Interlace image. - /// libspng row filter flag(s). - /// Quantise to 8bpp palette. - /// Quantisation quality. - /// Amount of dithering. - /// Write as a 1, 2, 4, 8 or 16 bit image. - /// Quantisation CPU effort. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void PngsaveTarget(Target target, int? compression = null, bool? interlace = null, Enums.ForeignPngFilter? filter = null, bool? palette = null, int? q = null, double? dither = null, int? bitdepth = null, int? effort = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(compression), compression); - options.AddIfPresent(nameof(interlace), interlace); - options.AddIfPresent(nameof(filter), filter); - options.AddIfPresent(nameof(palette), palette); - options.AddIfPresent("Q", q); - options.AddIfPresent(nameof(dither), dither); - options.AddIfPresent(nameof(bitdepth), bitdepth); - options.AddIfPresent(nameof(effort), effort); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("pngsave_target", options, target); - } - - /// - /// Save image to stream as PNG. - /// - /// - /// - /// in.PngsaveStream(stream, compression: int, interlace: bool, filter: Enums.ForeignPngFilter, palette: bool, q: int, dither: double, bitdepth: int, effort: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Stream to save to. - /// Compression factor. - /// Interlace image. - /// libspng row filter flag(s). - /// Quantise to 8bpp palette. - /// Quantisation quality. - /// Amount of dithering. - /// Write as a 1, 2, 4, 8 or 16 bit image. - /// Quantisation CPU effort. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void PngsaveStream(Stream stream, int? compression = null, bool? interlace = null, Enums.ForeignPngFilter? filter = null, bool? palette = null, int? q = null, double? dither = null, int? bitdepth = null, int? effort = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - using var target = TargetStream.NewFromStream(stream); - PngsaveTarget(target, compression, interlace, filter, palette, q, dither, bitdepth, effort, keep, background, pageHeight, profile); - } - - /// - /// Load ppm from file. - /// - /// - /// - /// using Image @out = NetVips.Image.Ppmload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Ppmload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("ppmload", options, filename) as Image; - } - - /// - /// Load ppm from file. - /// - /// - /// - /// using Image @out = NetVips.Image.Ppmload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Ppmload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("ppmload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load ppm base class. - /// - /// - /// - /// using Image @out = NetVips.Image.PpmloadSource(source, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image PpmloadSource(Source source, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("ppmload_source", options, source) as Image; - } - - /// - /// Load ppm base class. - /// - /// - /// - /// using Image @out = NetVips.Image.PpmloadStream(stream, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image PpmloadStream(Stream stream, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = PpmloadSource(source, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load ppm base class. - /// - /// - /// - /// using Image @out = NetVips.Image.PpmloadSource(source, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image PpmloadSource(Source source, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("ppmload_source", options, source) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load ppm base class. - /// - /// - /// - /// using Image @out = NetVips.Image.PpmloadStream(stream, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image PpmloadStream(Stream stream, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = PpmloadSource(source, out flags, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Save image to ppm file. - /// - /// - /// - /// in.Ppmsave(filename, format: Enums.ForeignPpmFormat, ascii: bool, bitdepth: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Filename to save to. - /// Format to save in. - /// Save as ascii. - /// Set to 1 to write as a 1 bit image. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Ppmsave(string filename, Enums.ForeignPpmFormat? format = null, bool? ascii = null, int? bitdepth = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(format), format); - options.AddIfPresent(nameof(ascii), ascii); - options.AddIfPresent(nameof(bitdepth), bitdepth); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("ppmsave", options, filename); - } - - /// - /// Save to ppm. - /// - /// - /// - /// in.PpmsaveTarget(target, format: Enums.ForeignPpmFormat, ascii: bool, bitdepth: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Target to save to. - /// Format to save in. - /// Save as ascii. - /// Set to 1 to write as a 1 bit image. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void PpmsaveTarget(Target target, Enums.ForeignPpmFormat? format = null, bool? ascii = null, int? bitdepth = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(format), format); - options.AddIfPresent(nameof(ascii), ascii); - options.AddIfPresent(nameof(bitdepth), bitdepth); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("ppmsave_target", options, target); - } - - /// - /// Save to ppm. - /// - /// - /// - /// in.PpmsaveStream(stream, format: Enums.ForeignPpmFormat, ascii: bool, bitdepth: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Stream to save to. - /// Format to save in. - /// Save as ascii. - /// Set to 1 to write as a 1 bit image. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void PpmsaveStream(Stream stream, Enums.ForeignPpmFormat? format = null, bool? ascii = null, int? bitdepth = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - using var target = TargetStream.NewFromStream(stream); - PpmsaveTarget(target, format, ascii, bitdepth, keep, background, pageHeight, profile); - } - - /// - /// Premultiply image alpha. - /// - /// - /// - /// using Image @out = in.Premultiply(maxAlpha: double); - /// - /// - /// Maximum value of alpha channel. - /// A new . - public Image Premultiply(double? maxAlpha = null) - { - var options = new VOption(); - - options.AddIfPresent("max_alpha", maxAlpha); - - return this.Call("premultiply", options) as Image; - } - - /// - /// Prewitt edge detector. - /// - /// - /// - /// using Image @out = in.Prewitt(); - /// - /// - /// A new . - public Image Prewitt() - { - return this.Call("prewitt") as Image; - } - - /// - /// Find image profiles. - /// - /// - /// - /// var output = in.Profile(); - /// - /// - /// An array of objects. - public object[] Profile() - { - return this.Call("profile") as object[]; - } - - /// - /// Load named ICC profile. - /// - /// - /// - /// byte[] profile = NetVips.Image.ProfileLoad(name); - /// - /// - /// Profile name. - /// An array of bytes. - public static byte[] ProfileLoad(string name) - { - return Operation.Call("profile_load", name) as byte[]; - } - - /// - /// Find image projections. - /// - /// - /// - /// var output = in.Project(); - /// - /// - /// An array of objects. - public object[] Project() - { - return this.Call("project") as object[]; - } - - /// - /// Resample an image with a quadratic transform. - /// - /// - /// - /// using Image @out = in.Quadratic(coeff, interpolate: GObject); - /// - /// - /// Coefficient matrix. - /// Interpolate values with this. - /// A new . - public Image Quadratic(Image coeff, GObject interpolate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(interpolate), interpolate); - - return this.Call("quadratic", options, coeff) as Image; - } - - /// - /// Unpack Radiance coding to float RGB. - /// - /// - /// - /// using Image @out = in.Rad2float(); - /// - /// - /// A new . - public Image Rad2float() - { - return this.Call("rad2float") as Image; - } - - /// - /// Load a Radiance image from a file. - /// - /// - /// - /// using Image @out = NetVips.Image.Radload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Radload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("radload", options, filename) as Image; - } - - /// - /// Load a Radiance image from a file. - /// - /// - /// - /// using Image @out = NetVips.Image.Radload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Radload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("radload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load rad from buffer. - /// - /// - /// - /// using Image @out = NetVips.Image.RadloadBuffer(buffer, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image RadloadBuffer(byte[] buffer, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("radload_buffer", options, buffer) as Image; - } - - /// - /// Load rad from buffer. - /// - /// - /// - /// using Image @out = NetVips.Image.RadloadBuffer(buffer, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image RadloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("radload_buffer", options, buffer) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load rad from source. - /// - /// - /// - /// using Image @out = NetVips.Image.RadloadSource(source, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image RadloadSource(Source source, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("radload_source", options, source) as Image; - } - - /// - /// Load rad from stream. - /// - /// - /// - /// using Image @out = NetVips.Image.RadloadStream(stream, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image RadloadStream(Stream stream, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = RadloadSource(source, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load rad from source. - /// - /// - /// - /// using Image @out = NetVips.Image.RadloadSource(source, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image RadloadSource(Source source, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("radload_source", options, source) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load rad from stream. - /// - /// - /// - /// using Image @out = NetVips.Image.RadloadStream(stream, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image RadloadStream(Stream stream, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = RadloadSource(source, out flags, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Save image to Radiance file. - /// - /// - /// - /// in.Radsave(filename, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Filename to save to. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Radsave(string filename, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("radsave", options, filename); - } - - /// - /// Save image to Radiance buffer. - /// - /// - /// - /// byte[] buffer = in.RadsaveBuffer(keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - /// An array of bytes. - public byte[] RadsaveBuffer(Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - return this.Call("radsave_buffer", options) as byte[]; - } - - /// - /// Save image to Radiance target. - /// - /// - /// - /// in.RadsaveTarget(target, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Target to save to. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void RadsaveTarget(Target target, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("radsave_target", options, target); - } - - /// - /// Save image to Radiance stream. - /// - /// - /// - /// in.RadsaveStream(stream, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Stream to save to. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void RadsaveStream(Stream stream, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - using var target = TargetStream.NewFromStream(stream); - RadsaveTarget(target, keep, background, pageHeight, profile); - } - - /// - /// Rank filter. - /// - /// - /// - /// using Image @out = in.Rank(width, height, index); - /// - /// - /// Window width in pixels. - /// Window height in pixels. - /// Select pixel at index. - /// A new . - public Image Rank(int width, int height, int index) - { - return this.Call("rank", width, height, index) as Image; - } - - /// - /// Load raw data from a file. - /// - /// - /// - /// using Image @out = NetVips.Image.Rawload(filename, width, height, bands, offset: ulong, format: Enums.BandFormat, interpretation: Enums.Interpretation, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Image width in pixels. - /// Image height in pixels. - /// Number of bands in image. - /// Offset in bytes from start of file. - /// Pixel format in image. - /// Pixel interpretation. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Rawload(string filename, int width, int height, int bands, ulong? offset = null, Enums.BandFormat? format = null, Enums.Interpretation? interpretation = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(offset), offset); - options.AddIfPresent(nameof(format), format); - options.AddIfPresent(nameof(interpretation), interpretation); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("rawload", options, filename, width, height, bands) as Image; - } - - /// - /// Load raw data from a file. - /// - /// - /// - /// using Image @out = NetVips.Image.Rawload(filename, width, height, bands, out var flags, offset: ulong, format: Enums.BandFormat, interpretation: Enums.Interpretation, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Image width in pixels. - /// Image height in pixels. - /// Number of bands in image. - /// Flags for this file. - /// Offset in bytes from start of file. - /// Pixel format in image. - /// Pixel interpretation. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Rawload(string filename, int width, int height, int bands, out Enums.ForeignFlags flags, ulong? offset = null, Enums.BandFormat? format = null, Enums.Interpretation? interpretation = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(offset), offset); - options.AddIfPresent(nameof(format), format); - options.AddIfPresent(nameof(interpretation), interpretation); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("rawload", options, filename, width, height, bands) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Save image to raw file. - /// - /// - /// - /// in.Rawsave(filename, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Filename to save to. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Rawsave(string filename, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("rawsave", options, filename); - } - - /// - /// Write raw image to file descriptor. - /// - /// - /// - /// in.RawsaveFd(fd, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// File descriptor to write to. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void RawsaveFd(int fd, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("rawsave_fd", options, fd); - } - - /// - /// Linear recombination with matrix. - /// - /// - /// - /// using Image @out = in.Recomb(m); - /// - /// - /// Matrix of coefficients. - /// A new . - public Image Recomb(Image m) - { - return this.Call("recomb", m) as Image; - } - - /// - /// Reduce an image. - /// - /// - /// - /// using Image @out = in.Reduce(hshrink, vshrink, kernel: Enums.Kernel, gap: double); - /// - /// - /// Horizontal shrink factor. - /// Vertical shrink factor. - /// Resampling kernel. - /// Reducing gap. - /// A new . - public Image Reduce(double hshrink, double vshrink, Enums.Kernel? kernel = null, double? gap = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(kernel), kernel); - options.AddIfPresent(nameof(gap), gap); - - return this.Call("reduce", options, hshrink, vshrink) as Image; - } - - /// - /// Shrink an image horizontally. - /// - /// - /// - /// using Image @out = in.Reduceh(hshrink, kernel: Enums.Kernel, gap: double); - /// - /// - /// Horizontal shrink factor. - /// Resampling kernel. - /// Reducing gap. - /// A new . - public Image Reduceh(double hshrink, Enums.Kernel? kernel = null, double? gap = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(kernel), kernel); - options.AddIfPresent(nameof(gap), gap); - - return this.Call("reduceh", options, hshrink) as Image; - } - - /// - /// Shrink an image vertically. - /// - /// - /// - /// using Image @out = in.Reducev(vshrink, kernel: Enums.Kernel, gap: double); - /// - /// - /// Vertical shrink factor. - /// Resampling kernel. - /// Reducing gap. - /// A new . - public Image Reducev(double vshrink, Enums.Kernel? kernel = null, double? gap = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(kernel), kernel); - options.AddIfPresent(nameof(gap), gap); - - return this.Call("reducev", options, vshrink) as Image; - } - - /// - /// Relational operation on two images. - /// - /// - /// - /// using Image @out = left.Relational(right, relational); - /// - /// - /// Right-hand image argument. - /// Relational to perform. - /// A new . - public Image Relational(Image right, Enums.OperationRelational relational) - { - return this.Call("relational", right, relational) as Image; - } - - /// - /// Relational operations against a constant. - /// - /// - /// - /// using Image @out = in.RelationalConst(relational, c); - /// - /// - /// Relational to perform. - /// Array of constants. - /// A new . - public Image RelationalConst(Enums.OperationRelational relational, double[] c) - { - return this.Call("relational_const", relational, c) as Image; - } - - /// - /// Remainder after integer division of two images. - /// - /// - /// - /// using Image @out = left.Remainder(right); - /// - /// - /// Right-hand image argument. - /// A new . - public Image Remainder(Image right) - { - return this.Call("remainder", right) as Image; - } - - /// - /// Remainder after integer division of an image and a constant. - /// - /// - /// - /// using Image @out = in.RemainderConst(c); - /// - /// - /// Array of constants. - /// A new . - public Image RemainderConst(double[] c) - { - return this.Call("remainder_const", c) as Image; - } - - /// - /// Replicate an image. - /// - /// - /// - /// using Image @out = in.Replicate(across, down); - /// - /// - /// Repeat this many times horizontally. - /// Repeat this many times vertically. - /// A new . - public Image Replicate(int across, int down) - { - return this.Call("replicate", across, down) as Image; - } - - /// - /// Resize an image. - /// - /// - /// - /// using Image @out = in.Resize(scale, kernel: Enums.Kernel, gap: double, vscale: double); - /// - /// - /// Scale image by this factor. - /// Resampling kernel. - /// Reducing gap. - /// Vertical scale image by this factor. - /// A new . - public Image Resize(double scale, Enums.Kernel? kernel = null, double? gap = null, double? vscale = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(kernel), kernel); - options.AddIfPresent(nameof(gap), gap); - options.AddIfPresent(nameof(vscale), vscale); - - return this.Call("resize", options, scale) as Image; - } - - /// - /// Rotate an image. - /// - /// - /// - /// using Image @out = in.Rot(angle); - /// - /// - /// Angle to rotate image. - /// A new . - public Image Rot(Enums.Angle angle) - { - return this.Call("rot", angle) as Image; - } - - /// - /// Rotate an image. - /// - /// - /// - /// using Image @out = in.Rot45(angle: Enums.Angle45); - /// - /// - /// Angle to rotate image. - /// A new . - public Image Rot45(Enums.Angle45? angle = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(angle), angle); - - return this.Call("rot45", options) as Image; - } - - /// - /// Rotate an image by a number of degrees. - /// - /// - /// - /// using Image @out = in.Rotate(angle, interpolate: GObject, background: double[], odx: double, ody: double, idx: double, idy: double); - /// - /// - /// Rotate anticlockwise by this many degrees. - /// Interpolate pixels with this. - /// Background value. - /// Horizontal output displacement. - /// Vertical output displacement. - /// Horizontal input displacement. - /// Vertical input displacement. - /// A new . - public Image Rotate(double angle, GObject interpolate = null, double[] background = null, double? odx = null, double? ody = null, double? idx = null, double? idy = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(interpolate), interpolate); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent(nameof(odx), odx); - options.AddIfPresent(nameof(ody), ody); - options.AddIfPresent(nameof(idx), idx); - options.AddIfPresent(nameof(idy), idy); - - return this.Call("rotate", options, angle) as Image; - } - - /// - /// Perform a round function on an image. - /// - /// - /// - /// using Image @out = in.Round(round); - /// - /// - /// Rounding operation to perform. - /// A new . - public Image Round(Enums.OperationRound round) - { - return this.Call("round", round) as Image; - } - - /// - /// Scharr edge detector. - /// - /// - /// - /// using Image @out = in.Scharr(); - /// - /// - /// A new . - public Image Scharr() - { - return this.Call("scharr") as Image; - } - - /// - /// Convert scRGB to BW. - /// - /// - /// - /// using Image @out = in.ScRGB2BW(depth: int); - /// - /// - /// Output device space depth in bits. - /// A new . - public Image ScRGB2BW(int? depth = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(depth), depth); - - return this.Call("scRGB2BW", options) as Image; - } - - /// - /// Convert an scRGB image to sRGB. - /// - /// - /// - /// using Image @out = in.ScRGB2sRGB(depth: int); - /// - /// - /// Output device space depth in bits. - /// A new . - public Image ScRGB2sRGB(int? depth = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(depth), depth); - - return this.Call("scRGB2sRGB", options) as Image; - } - - /// - /// Transform scRGB to XYZ. - /// - /// - /// - /// using Image @out = in.ScRGB2XYZ(); - /// - /// - /// A new . - public Image ScRGB2XYZ() - { - return this.Call("scRGB2XYZ") as Image; - } - - /// - /// Check sequential access. - /// - /// - /// - /// using Image @out = in.Sequential(tileHeight: int); - /// - /// - /// Tile height in pixels. - /// A new . - public Image Sequential(int? tileHeight = null) - { - var options = new VOption(); - - options.AddIfPresent("tile_height", tileHeight); - - return this.Call("sequential", options) as Image; - } - - /// - /// Unsharp masking for print. - /// - /// - /// - /// using Image @out = in.Sharpen(sigma: double, x1: double, y2: double, y3: double, m1: double, m2: double); - /// - /// - /// Sigma of Gaussian. - /// Flat/jaggy threshold. - /// Maximum brightening. - /// Maximum darkening. - /// Slope for flat areas. - /// Slope for jaggy areas. - /// A new . - public Image Sharpen(double? sigma = null, double? x1 = null, double? y2 = null, double? y3 = null, double? m1 = null, double? m2 = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(sigma), sigma); - options.AddIfPresent(nameof(x1), x1); - options.AddIfPresent(nameof(y2), y2); - options.AddIfPresent(nameof(y3), y3); - options.AddIfPresent(nameof(m1), m1); - options.AddIfPresent(nameof(m2), m2); - - return this.Call("sharpen", options) as Image; - } - - /// - /// Shrink an image. - /// - /// - /// - /// using Image @out = in.Shrink(hshrink, vshrink, ceil: bool); - /// - /// - /// Horizontal shrink factor. - /// Vertical shrink factor. - /// Round-up output dimensions. - /// A new . - public Image Shrink(double hshrink, double vshrink, bool? ceil = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(ceil), ceil); - - return this.Call("shrink", options, hshrink, vshrink) as Image; - } - - /// - /// Shrink an image horizontally. - /// - /// - /// - /// using Image @out = in.Shrinkh(hshrink, ceil: bool); - /// - /// - /// Horizontal shrink factor. - /// Round-up output dimensions. - /// A new . - public Image Shrinkh(int hshrink, bool? ceil = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(ceil), ceil); - - return this.Call("shrinkh", options, hshrink) as Image; - } - - /// - /// Shrink an image vertically. - /// - /// - /// - /// using Image @out = in.Shrinkv(vshrink, ceil: bool); - /// - /// - /// Vertical shrink factor. - /// Round-up output dimensions. - /// A new . - public Image Shrinkv(int vshrink, bool? ceil = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(ceil), ceil); - - return this.Call("shrinkv", options, vshrink) as Image; - } - - /// - /// Unit vector of pixel. - /// - /// - /// - /// using Image @out = in.Sign(); - /// - /// - /// A new . - public Image Sign() - { - return this.Call("sign") as Image; - } - - /// - /// Similarity transform of an image. - /// - /// - /// - /// using Image @out = in.Similarity(scale: double, angle: double, interpolate: GObject, background: double[], odx: double, ody: double, idx: double, idy: double); - /// - /// - /// Scale by this factor. - /// Rotate anticlockwise by this many degrees. - /// Interpolate pixels with this. - /// Background value. - /// Horizontal output displacement. - /// Vertical output displacement. - /// Horizontal input displacement. - /// Vertical input displacement. - /// A new . - public Image Similarity(double? scale = null, double? angle = null, GObject interpolate = null, double[] background = null, double? odx = null, double? ody = null, double? idx = null, double? idy = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(angle), angle); - options.AddIfPresent(nameof(interpolate), interpolate); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent(nameof(odx), odx); - options.AddIfPresent(nameof(ody), ody); - options.AddIfPresent(nameof(idx), idx); - options.AddIfPresent(nameof(idy), idy); - - return this.Call("similarity", options) as Image; - } - - /// - /// Make a 2D sine wave. - /// - /// - /// - /// using Image @out = NetVips.Image.Sines(width, height, uchar: bool, hfreq: double, vfreq: double); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Output an unsigned char image. - /// Horizontal spatial frequency. - /// Vertical spatial frequency. - /// A new . - public static Image Sines(int width, int height, bool? uchar = null, double? hfreq = null, double? vfreq = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(uchar), uchar); - options.AddIfPresent(nameof(hfreq), hfreq); - options.AddIfPresent(nameof(vfreq), vfreq); - - return Operation.Call("sines", options, width, height) as Image; - } - - /// - /// Extract an area from an image. - /// - /// - /// - /// using Image @out = input.Smartcrop(width, height, interesting: Enums.Interesting, premultiplied: bool); - /// - /// - /// Width of extract area. - /// Height of extract area. - /// How to measure interestingness. - /// Input image already has premultiplied alpha. - /// A new . - public Image Smartcrop(int width, int height, Enums.Interesting? interesting = null, bool? premultiplied = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(interesting), interesting); - options.AddIfPresent(nameof(premultiplied), premultiplied); - - return this.Call("smartcrop", options, width, height) as Image; - } - - /// - /// Extract an area from an image. - /// - /// - /// - /// using Image @out = input.Smartcrop(width, height, out var attentionX, interesting: Enums.Interesting, premultiplied: bool); - /// - /// - /// Width of extract area. - /// Height of extract area. - /// Horizontal position of attention centre. - /// How to measure interestingness. - /// Input image already has premultiplied alpha. - /// A new . - public Image Smartcrop(int width, int height, out int attentionX, Enums.Interesting? interesting = null, bool? premultiplied = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(interesting), interesting); - options.AddIfPresent(nameof(premultiplied), premultiplied); - - options.Add("attention_x", true); - - var results = this.Call("smartcrop", options, width, height) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - attentionX = opts?["attention_x"] is int out1 ? out1 : 0; - - return finalResult; - } - - /// - /// Extract an area from an image. - /// - /// - /// - /// using Image @out = input.Smartcrop(width, height, out var attentionX, out var attentionY, interesting: Enums.Interesting, premultiplied: bool); - /// - /// - /// Width of extract area. - /// Height of extract area. - /// Horizontal position of attention centre. - /// Vertical position of attention centre. - /// How to measure interestingness. - /// Input image already has premultiplied alpha. - /// A new . - public Image Smartcrop(int width, int height, out int attentionX, out int attentionY, Enums.Interesting? interesting = null, bool? premultiplied = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(interesting), interesting); - options.AddIfPresent(nameof(premultiplied), premultiplied); - - options.Add("attention_x", true); - options.Add("attention_y", true); - - var results = this.Call("smartcrop", options, width, height) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - attentionX = opts?["attention_x"] is int out1 ? out1 : 0; - attentionY = opts?["attention_y"] is int out2 ? out2 : 0; - - return finalResult; - } - - /// - /// Sobel edge detector. - /// - /// - /// - /// using Image @out = in.Sobel(); - /// - /// - /// A new . - public Image Sobel() - { - return this.Call("sobel") as Image; - } - - /// - /// Spatial correlation. - /// - /// - /// - /// using Image @out = in.Spcor(@ref); - /// - /// - /// Input reference image. - /// A new . - public Image Spcor(Image @ref) - { - return this.Call("spcor", @ref) as Image; - } - - /// - /// Make displayable power spectrum. - /// - /// - /// - /// using Image @out = in.Spectrum(); - /// - /// - /// A new . - public Image Spectrum() - { - return this.Call("spectrum") as Image; - } - - /// - /// Transform sRGB to HSV. - /// - /// - /// - /// using Image @out = in.SRGB2HSV(); - /// - /// - /// A new . - public Image SRGB2HSV() - { - return this.Call("sRGB2HSV") as Image; - } - - /// - /// Convert an sRGB image to scRGB. - /// - /// - /// - /// using Image @out = in.SRGB2scRGB(); - /// - /// - /// A new . - public Image SRGB2scRGB() - { - return this.Call("sRGB2scRGB") as Image; - } - - /// - /// Find many image stats. - /// - /// - /// - /// using Image @out = in.Stats(); - /// - /// - /// A new . - public Image Stats() - { - return this.Call("stats") as Image; - } - - /// - /// Statistical difference. - /// - /// - /// - /// using Image @out = in.Stdif(width, height, s0: double, b: double, m0: double, a: double); - /// - /// - /// Window width in pixels. - /// Window height in pixels. - /// New deviation. - /// Weight of new deviation. - /// New mean. - /// Weight of new mean. - /// A new . - public Image Stdif(int width, int height, double? s0 = null, double? b = null, double? m0 = null, double? a = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(s0), s0); - options.AddIfPresent(nameof(b), b); - options.AddIfPresent(nameof(m0), m0); - options.AddIfPresent(nameof(a), a); - - return this.Call("stdif", options, width, height) as Image; - } - - /// - /// Subsample an image. - /// - /// - /// - /// using Image @out = input.Subsample(xfac, yfac, point: bool); - /// - /// - /// Horizontal subsample factor. - /// Vertical subsample factor. - /// Point sample. - /// A new . - public Image Subsample(int xfac, int yfac, bool? point = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(point), point); - - return this.Call("subsample", options, xfac, yfac) as Image; - } - - /// - /// Subtract two images. - /// - /// - /// - /// using Image @out = left.Subtract(right); - /// - /// - /// Right-hand image argument. - /// A new . - public Image Subtract(Image right) - { - return this.Call("subtract", right) as Image; - } - - /// - /// Sum an array of images. - /// - /// - /// - /// using Image @out = NetVips.Image.Sum(@in); - /// - /// - /// Array of input images. - /// A new . - public static Image Sum(params Image[] @in) - { - return Operation.Call("sum", new object[] { @in }) as Image; - } - - /// - /// Load SVG with rsvg. - /// - /// - /// - /// using Image @out = NetVips.Image.Svgload(filename, dpi: double, scale: double, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Render at this DPI. - /// Scale output by this factor. - /// Allow SVG of any size. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Svgload(string filename, double? dpi = null, double? scale = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(dpi), dpi); - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("svgload", options, filename) as Image; - } - - /// - /// Load SVG with rsvg. - /// - /// - /// - /// using Image @out = NetVips.Image.Svgload(filename, out var flags, dpi: double, scale: double, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// Render at this DPI. - /// Scale output by this factor. - /// Allow SVG of any size. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Svgload(string filename, out Enums.ForeignFlags flags, double? dpi = null, double? scale = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(dpi), dpi); - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("svgload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load SVG with rsvg. - /// - /// - /// - /// using Image @out = NetVips.Image.SvgloadBuffer(buffer, dpi: double, scale: double, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Render at this DPI. - /// Scale output by this factor. - /// Allow SVG of any size. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image SvgloadBuffer(byte[] buffer, double? dpi = null, double? scale = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(dpi), dpi); - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("svgload_buffer", options, buffer) as Image; - } - - /// - /// Load SVG with rsvg. - /// - /// - /// - /// using Image @out = NetVips.Image.SvgloadBuffer(buffer, out var flags, dpi: double, scale: double, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Flags for this file. - /// Render at this DPI. - /// Scale output by this factor. - /// Allow SVG of any size. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image SvgloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, double? dpi = null, double? scale = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(dpi), dpi); - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("svgload_buffer", options, buffer) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load svg from source. - /// - /// - /// - /// using Image @out = NetVips.Image.SvgloadSource(source, dpi: double, scale: double, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Render at this DPI. - /// Scale output by this factor. - /// Allow SVG of any size. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image SvgloadSource(Source source, double? dpi = null, double? scale = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(dpi), dpi); - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("svgload_source", options, source) as Image; - } - - /// - /// Load svg from stream. - /// - /// - /// - /// using Image @out = NetVips.Image.SvgloadStream(stream, dpi: double, scale: double, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Render at this DPI. - /// Scale output by this factor. - /// Allow SVG of any size. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image SvgloadStream(Stream stream, double? dpi = null, double? scale = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = SvgloadSource(source, dpi, scale, unlimited, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load svg from source. - /// - /// - /// - /// using Image @out = NetVips.Image.SvgloadSource(source, out var flags, dpi: double, scale: double, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Flags for this file. - /// Render at this DPI. - /// Scale output by this factor. - /// Allow SVG of any size. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image SvgloadSource(Source source, out Enums.ForeignFlags flags, double? dpi = null, double? scale = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(dpi), dpi); - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(unlimited), unlimited); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("svgload_source", options, source) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load svg from stream. - /// - /// - /// - /// using Image @out = NetVips.Image.SvgloadStream(stream, out var flags, dpi: double, scale: double, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Flags for this file. - /// Render at this DPI. - /// Scale output by this factor. - /// Allow SVG of any size. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image SvgloadStream(Stream stream, out Enums.ForeignFlags flags, double? dpi = null, double? scale = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = SvgloadSource(source, out flags, dpi, scale, unlimited, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Find the index of the first non-zero pixel in tests. - /// - /// - /// - /// using Image @out = NetVips.Image.Switch(tests); - /// - /// - /// Table of images to test. - /// A new . - public static Image Switch(params Image[] tests) - { - return Operation.Call("switch", new object[] { tests }) as Image; - } - - /// - /// Run an external command. - /// - /// - /// - /// NetVips.Image.System(cmdFormat, @in: Image[], outFormat: string, inFormat: string); - /// - /// - /// Command to run. - /// Array of input images. - /// Format for output filename. - /// Format for input filename. - public static void System(string cmdFormat, Image[] @in = null, string outFormat = null, string inFormat = null) - { - var options = new VOption(); - - options.AddIfPresent("in", @in); - options.AddIfPresent("out_format", outFormat); - options.AddIfPresent("in_format", inFormat); - - Operation.Call("system", options, cmdFormat); - } - - /// - /// Run an external command. - /// - /// - /// - /// NetVips.Image.System(cmdFormat, out var @out, @in: Image[], outFormat: string, inFormat: string); - /// - /// - /// Command to run. - /// Output image. - /// Array of input images. - /// Format for output filename. - /// Format for input filename. - public static void System(string cmdFormat, out Image @out, Image[] @in = null, string outFormat = null, string inFormat = null) - { - var options = new VOption(); - - options.AddIfPresent("in", @in); - options.AddIfPresent("out_format", outFormat); - options.AddIfPresent("in_format", inFormat); - - options.Add("out", true); - - var results = Operation.Call("system", options, cmdFormat) as object[]; - - var opts = results?[1] as VOption; - @out = opts?["out"] as Image; - } - - /// - /// Run an external command. - /// - /// - /// - /// NetVips.Image.System(cmdFormat, out var @out, out var log, @in: Image[], outFormat: string, inFormat: string); - /// - /// - /// Command to run. - /// Output image. - /// Command log. - /// Array of input images. - /// Format for output filename. - /// Format for input filename. - public static void System(string cmdFormat, out Image @out, out string log, Image[] @in = null, string outFormat = null, string inFormat = null) - { - var options = new VOption(); - - options.AddIfPresent("in", @in); - options.AddIfPresent("out_format", outFormat); - options.AddIfPresent("in_format", inFormat); - - options.Add("out", true); - options.Add("log", true); - - var results = Operation.Call("system", options, cmdFormat) as object[]; - - var opts = results?[1] as VOption; - @out = opts?["out"] as Image; - log = opts?["log"] is string out2 ? out2 : null; - } - - /// - /// Make a text image. - /// - /// - /// - /// using Image @out = NetVips.Image.Text(text, font: string, width: int, height: int, align: Enums.Align, justify: bool, dpi: int, spacing: int, fontfile: string, rgba: bool, wrap: Enums.TextWrap); - /// - /// - /// Text to render. - /// Font to render with. - /// Maximum image width in pixels. - /// Maximum image height in pixels. - /// Align on the low, centre or high edge. - /// Justify lines. - /// DPI to render at. - /// Line spacing. - /// Load this font file. - /// Enable RGBA output. - /// Wrap lines on word or character boundaries. - /// A new . - public static Image Text(string text, string font = null, int? width = null, int? height = null, Enums.Align? align = null, bool? justify = null, int? dpi = null, int? spacing = null, string fontfile = null, bool? rgba = null, Enums.TextWrap? wrap = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(font), font); - options.AddIfPresent(nameof(width), width); - options.AddIfPresent(nameof(height), height); - options.AddIfPresent(nameof(align), align); - options.AddIfPresent(nameof(justify), justify); - options.AddIfPresent(nameof(dpi), dpi); - options.AddIfPresent(nameof(spacing), spacing); - options.AddIfPresent(nameof(fontfile), fontfile); - options.AddIfPresent(nameof(rgba), rgba); - options.AddIfPresent(nameof(wrap), wrap); - - return Operation.Call("text", options, text) as Image; - } - - /// - /// Make a text image. - /// - /// - /// - /// using Image @out = NetVips.Image.Text(text, out var autofitDpi, font: string, width: int, height: int, align: Enums.Align, justify: bool, dpi: int, spacing: int, fontfile: string, rgba: bool, wrap: Enums.TextWrap); - /// - /// - /// Text to render. - /// DPI selected by autofit. - /// Font to render with. - /// Maximum image width in pixels. - /// Maximum image height in pixels. - /// Align on the low, centre or high edge. - /// Justify lines. - /// DPI to render at. - /// Line spacing. - /// Load this font file. - /// Enable RGBA output. - /// Wrap lines on word or character boundaries. - /// A new . - public static Image Text(string text, out int autofitDpi, string font = null, int? width = null, int? height = null, Enums.Align? align = null, bool? justify = null, int? dpi = null, int? spacing = null, string fontfile = null, bool? rgba = null, Enums.TextWrap? wrap = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(font), font); - options.AddIfPresent(nameof(width), width); - options.AddIfPresent(nameof(height), height); - options.AddIfPresent(nameof(align), align); - options.AddIfPresent(nameof(justify), justify); - options.AddIfPresent(nameof(dpi), dpi); - options.AddIfPresent(nameof(spacing), spacing); - options.AddIfPresent(nameof(fontfile), fontfile); - options.AddIfPresent(nameof(rgba), rgba); - options.AddIfPresent(nameof(wrap), wrap); - - options.Add("autofit_dpi", true); - - var results = Operation.Call("text", options, text) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - autofitDpi = opts?["autofit_dpi"] is int out1 ? out1 : 0; - - return finalResult; - } - - /// - /// Generate thumbnail from file. - /// - /// - /// - /// using Image @out = NetVips.Image.Thumbnail(filename, width, height: int, size: Enums.Size, noRotate: bool, crop: Enums.Interesting, linear: bool, importProfile: string, exportProfile: string, intent: Enums.Intent, failOn: Enums.FailOn); - /// - /// - /// Filename to read from. - /// Size to this width. - /// Size to this height. - /// Only upsize, only downsize, or both. - /// Don't use orientation tags to rotate image upright. - /// Reduce to fill target rectangle, then crop. - /// Reduce in linear light. - /// Fallback import profile. - /// Fallback export profile. - /// Rendering intent. - /// Error level to fail on. - /// A new . - public static Image Thumbnail(string filename, int width, int? height = null, Enums.Size? size = null, bool? noRotate = null, Enums.Interesting? crop = null, bool? linear = null, string importProfile = null, string exportProfile = null, Enums.Intent? intent = null, Enums.FailOn? failOn = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(height), height); - options.AddIfPresent(nameof(size), size); - options.AddIfPresent("no_rotate", noRotate); - options.AddIfPresent(nameof(crop), crop); - options.AddIfPresent(nameof(linear), linear); - options.AddIfPresent("import_profile", importProfile); - options.AddIfPresent("export_profile", exportProfile); - options.AddIfPresent(nameof(intent), intent); - options.AddFailOn(failOn); - - return Operation.Call("thumbnail", options, filename, width) as Image; - } - - /// - /// Generate thumbnail from buffer. - /// - /// - /// - /// using Image @out = NetVips.Image.ThumbnailBuffer(buffer, width, optionString: string, height: int, size: Enums.Size, noRotate: bool, crop: Enums.Interesting, linear: bool, importProfile: string, exportProfile: string, intent: Enums.Intent, failOn: Enums.FailOn); - /// - /// - /// Buffer to load from. - /// Size to this width. - /// Options that are passed on to the underlying loader. - /// Size to this height. - /// Only upsize, only downsize, or both. - /// Don't use orientation tags to rotate image upright. - /// Reduce to fill target rectangle, then crop. - /// Reduce in linear light. - /// Fallback import profile. - /// Fallback export profile. - /// Rendering intent. - /// Error level to fail on. - /// A new . - public static Image ThumbnailBuffer(byte[] buffer, int width, string optionString = null, int? height = null, Enums.Size? size = null, bool? noRotate = null, Enums.Interesting? crop = null, bool? linear = null, string importProfile = null, string exportProfile = null, Enums.Intent? intent = null, Enums.FailOn? failOn = null) - { - var options = new VOption(); - - options.AddIfPresent("option_string", optionString); - options.AddIfPresent(nameof(height), height); - options.AddIfPresent(nameof(size), size); - options.AddIfPresent("no_rotate", noRotate); - options.AddIfPresent(nameof(crop), crop); - options.AddIfPresent(nameof(linear), linear); - options.AddIfPresent("import_profile", importProfile); - options.AddIfPresent("export_profile", exportProfile); - options.AddIfPresent(nameof(intent), intent); - options.AddFailOn(failOn); - - return Operation.Call("thumbnail_buffer", options, buffer, width) as Image; - } - - /// - /// Generate thumbnail from image. - /// - /// - /// - /// using Image @out = in.ThumbnailImage(width, height: int, size: Enums.Size, noRotate: bool, crop: Enums.Interesting, linear: bool, importProfile: string, exportProfile: string, intent: Enums.Intent, failOn: Enums.FailOn); - /// - /// - /// Size to this width. - /// Size to this height. - /// Only upsize, only downsize, or both. - /// Don't use orientation tags to rotate image upright. - /// Reduce to fill target rectangle, then crop. - /// Reduce in linear light. - /// Fallback import profile. - /// Fallback export profile. - /// Rendering intent. - /// Error level to fail on. - /// A new . - public Image ThumbnailImage(int width, int? height = null, Enums.Size? size = null, bool? noRotate = null, Enums.Interesting? crop = null, bool? linear = null, string importProfile = null, string exportProfile = null, Enums.Intent? intent = null, Enums.FailOn? failOn = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(height), height); - options.AddIfPresent(nameof(size), size); - options.AddIfPresent("no_rotate", noRotate); - options.AddIfPresent(nameof(crop), crop); - options.AddIfPresent(nameof(linear), linear); - options.AddIfPresent("import_profile", importProfile); - options.AddIfPresent("export_profile", exportProfile); - options.AddIfPresent(nameof(intent), intent); - options.AddFailOn(failOn); - - return this.Call("thumbnail_image", options, width) as Image; - } - - /// - /// Generate thumbnail from source. - /// - /// - /// - /// using Image @out = NetVips.Image.ThumbnailSource(source, width, optionString: string, height: int, size: Enums.Size, noRotate: bool, crop: Enums.Interesting, linear: bool, importProfile: string, exportProfile: string, intent: Enums.Intent, failOn: Enums.FailOn); - /// - /// - /// Source to load from. - /// Size to this width. - /// Options that are passed on to the underlying loader. - /// Size to this height. - /// Only upsize, only downsize, or both. - /// Don't use orientation tags to rotate image upright. - /// Reduce to fill target rectangle, then crop. - /// Reduce in linear light. - /// Fallback import profile. - /// Fallback export profile. - /// Rendering intent. - /// Error level to fail on. - /// A new . - public static Image ThumbnailSource(Source source, int width, string optionString = null, int? height = null, Enums.Size? size = null, bool? noRotate = null, Enums.Interesting? crop = null, bool? linear = null, string importProfile = null, string exportProfile = null, Enums.Intent? intent = null, Enums.FailOn? failOn = null) - { - var options = new VOption(); - - options.AddIfPresent("option_string", optionString); - options.AddIfPresent(nameof(height), height); - options.AddIfPresent(nameof(size), size); - options.AddIfPresent("no_rotate", noRotate); - options.AddIfPresent(nameof(crop), crop); - options.AddIfPresent(nameof(linear), linear); - options.AddIfPresent("import_profile", importProfile); - options.AddIfPresent("export_profile", exportProfile); - options.AddIfPresent(nameof(intent), intent); - options.AddFailOn(failOn); - - return Operation.Call("thumbnail_source", options, source, width) as Image; - } - - /// - /// Generate thumbnail from stream. - /// - /// - /// - /// using Image @out = NetVips.Image.ThumbnailStream(stream, width, optionString: string, height: int, size: Enums.Size, noRotate: bool, crop: Enums.Interesting, linear: bool, importProfile: string, exportProfile: string, intent: Enums.Intent, failOn: Enums.FailOn); - /// - /// - /// Stream to load from. - /// Size to this width. - /// Options that are passed on to the underlying loader. - /// Size to this height. - /// Only upsize, only downsize, or both. - /// Don't use orientation tags to rotate image upright. - /// Reduce to fill target rectangle, then crop. - /// Reduce in linear light. - /// Fallback import profile. - /// Fallback export profile. - /// Rendering intent. - /// Error level to fail on. - /// A new . - public static Image ThumbnailStream(Stream stream, int width, string optionString = null, int? height = null, Enums.Size? size = null, bool? noRotate = null, Enums.Interesting? crop = null, bool? linear = null, string importProfile = null, string exportProfile = null, Enums.Intent? intent = null, Enums.FailOn? failOn = null) - { - var source = SourceStream.NewFromStream(stream); - var image = ThumbnailSource(source, width, optionString, height, size, noRotate, crop, linear, importProfile, exportProfile, intent, failOn); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load tiff from file. - /// - /// - /// - /// using Image @out = NetVips.Image.Tiffload(filename, page: int, subifd: int, n: int, autorotate: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// First page to load. - /// Subifd index. - /// Number of pages to load, -1 for all. - /// Rotate image using orientation tag. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Tiffload(string filename, int? page = null, int? subifd = null, int? n = null, bool? autorotate = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(subifd), subifd); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(autorotate), autorotate); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("tiffload", options, filename) as Image; - } - - /// - /// Load tiff from file. - /// - /// - /// - /// using Image @out = NetVips.Image.Tiffload(filename, out var flags, page: int, subifd: int, n: int, autorotate: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// First page to load. - /// Subifd index. - /// Number of pages to load, -1 for all. - /// Rotate image using orientation tag. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Tiffload(string filename, out Enums.ForeignFlags flags, int? page = null, int? subifd = null, int? n = null, bool? autorotate = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(subifd), subifd); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(autorotate), autorotate); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("tiffload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load tiff from buffer. - /// - /// - /// - /// using Image @out = NetVips.Image.TiffloadBuffer(buffer, page: int, subifd: int, n: int, autorotate: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// First page to load. - /// Subifd index. - /// Number of pages to load, -1 for all. - /// Rotate image using orientation tag. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image TiffloadBuffer(byte[] buffer, int? page = null, int? subifd = null, int? n = null, bool? autorotate = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(subifd), subifd); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(autorotate), autorotate); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("tiffload_buffer", options, buffer) as Image; - } - - /// - /// Load tiff from buffer. - /// - /// - /// - /// using Image @out = NetVips.Image.TiffloadBuffer(buffer, out var flags, page: int, subifd: int, n: int, autorotate: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Flags for this file. - /// First page to load. - /// Subifd index. - /// Number of pages to load, -1 for all. - /// Rotate image using orientation tag. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image TiffloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, int? page = null, int? subifd = null, int? n = null, bool? autorotate = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(subifd), subifd); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(autorotate), autorotate); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("tiffload_buffer", options, buffer) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load tiff from source. - /// - /// - /// - /// using Image @out = NetVips.Image.TiffloadSource(source, page: int, subifd: int, n: int, autorotate: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// First page to load. - /// Subifd index. - /// Number of pages to load, -1 for all. - /// Rotate image using orientation tag. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image TiffloadSource(Source source, int? page = null, int? subifd = null, int? n = null, bool? autorotate = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(subifd), subifd); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(autorotate), autorotate); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("tiffload_source", options, source) as Image; - } - - /// - /// Load tiff from stream. - /// - /// - /// - /// using Image @out = NetVips.Image.TiffloadStream(stream, page: int, subifd: int, n: int, autorotate: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// First page to load. - /// Subifd index. - /// Number of pages to load, -1 for all. - /// Rotate image using orientation tag. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image TiffloadStream(Stream stream, int? page = null, int? subifd = null, int? n = null, bool? autorotate = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = TiffloadSource(source, page, subifd, n, autorotate, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load tiff from source. - /// - /// - /// - /// using Image @out = NetVips.Image.TiffloadSource(source, out var flags, page: int, subifd: int, n: int, autorotate: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Flags for this file. - /// First page to load. - /// Subifd index. - /// Number of pages to load, -1 for all. - /// Rotate image using orientation tag. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image TiffloadSource(Source source, out Enums.ForeignFlags flags, int? page = null, int? subifd = null, int? n = null, bool? autorotate = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(subifd), subifd); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(autorotate), autorotate); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("tiffload_source", options, source) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load tiff from stream. - /// - /// - /// - /// using Image @out = NetVips.Image.TiffloadStream(stream, out var flags, page: int, subifd: int, n: int, autorotate: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Flags for this file. - /// First page to load. - /// Subifd index. - /// Number of pages to load, -1 for all. - /// Rotate image using orientation tag. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image TiffloadStream(Stream stream, out Enums.ForeignFlags flags, int? page = null, int? subifd = null, int? n = null, bool? autorotate = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = TiffloadSource(source, out flags, page, subifd, n, autorotate, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Save image to tiff file. - /// - /// - /// - /// in.Tiffsave(filename, compression: Enums.ForeignTiffCompression, q: int, predictor: Enums.ForeignTiffPredictor, tile: bool, tileWidth: int, tileHeight: int, pyramid: bool, miniswhite: bool, bitdepth: int, resunit: Enums.ForeignTiffResunit, xres: double, yres: double, bigtiff: bool, properties: bool, regionShrink: Enums.RegionShrink, level: int, lossless: bool, depth: Enums.ForeignDzDepth, subifd: bool, premultiply: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Filename to save to. - /// Compression for this file. - /// Q factor. - /// Compression prediction. - /// Write a tiled tiff. - /// Tile width in pixels. - /// Tile height in pixels. - /// Write a pyramidal tiff. - /// Use 0 for white in 1-bit images. - /// Write as a 1, 2, 4 or 8 bit image. - /// Resolution unit. - /// Horizontal resolution in pixels/mm. - /// Vertical resolution in pixels/mm. - /// Write a bigtiff image. - /// Write a properties document to IMAGEDESCRIPTION. - /// Method to shrink regions. - /// ZSTD compression level. - /// Enable WEBP lossless mode. - /// Pyramid depth. - /// Save pyr layers as sub-IFDs. - /// Save with premultiplied alpha. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Tiffsave(string filename, Enums.ForeignTiffCompression? compression = null, int? q = null, Enums.ForeignTiffPredictor? predictor = null, bool? tile = null, int? tileWidth = null, int? tileHeight = null, bool? pyramid = null, bool? miniswhite = null, int? bitdepth = null, Enums.ForeignTiffResunit? resunit = null, double? xres = null, double? yres = null, bool? bigtiff = null, bool? properties = null, Enums.RegionShrink? regionShrink = null, int? level = null, bool? lossless = null, Enums.ForeignDzDepth? depth = null, bool? subifd = null, bool? premultiply = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(compression), compression); - options.AddIfPresent("Q", q); - options.AddIfPresent(nameof(predictor), predictor); - options.AddIfPresent(nameof(tile), tile); - options.AddIfPresent("tile_width", tileWidth); - options.AddIfPresent("tile_height", tileHeight); - options.AddIfPresent(nameof(pyramid), pyramid); - options.AddIfPresent(nameof(miniswhite), miniswhite); - options.AddIfPresent(nameof(bitdepth), bitdepth); - options.AddIfPresent(nameof(resunit), resunit); - options.AddIfPresent(nameof(xres), xres); - options.AddIfPresent(nameof(yres), yres); - options.AddIfPresent(nameof(bigtiff), bigtiff); - options.AddIfPresent(nameof(properties), properties); - options.AddIfPresent("region_shrink", regionShrink); - options.AddIfPresent(nameof(level), level); - options.AddIfPresent(nameof(lossless), lossless); - options.AddIfPresent(nameof(depth), depth); - options.AddIfPresent(nameof(subifd), subifd); - options.AddIfPresent(nameof(premultiply), premultiply); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("tiffsave", options, filename); - } - - /// - /// Save image to tiff buffer. - /// - /// - /// - /// byte[] buffer = in.TiffsaveBuffer(compression: Enums.ForeignTiffCompression, q: int, predictor: Enums.ForeignTiffPredictor, tile: bool, tileWidth: int, tileHeight: int, pyramid: bool, miniswhite: bool, bitdepth: int, resunit: Enums.ForeignTiffResunit, xres: double, yres: double, bigtiff: bool, properties: bool, regionShrink: Enums.RegionShrink, level: int, lossless: bool, depth: Enums.ForeignDzDepth, subifd: bool, premultiply: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Compression for this file. - /// Q factor. - /// Compression prediction. - /// Write a tiled tiff. - /// Tile width in pixels. - /// Tile height in pixels. - /// Write a pyramidal tiff. - /// Use 0 for white in 1-bit images. - /// Write as a 1, 2, 4 or 8 bit image. - /// Resolution unit. - /// Horizontal resolution in pixels/mm. - /// Vertical resolution in pixels/mm. - /// Write a bigtiff image. - /// Write a properties document to IMAGEDESCRIPTION. - /// Method to shrink regions. - /// ZSTD compression level. - /// Enable WEBP lossless mode. - /// Pyramid depth. - /// Save pyr layers as sub-IFDs. - /// Save with premultiplied alpha. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - /// An array of bytes. - public byte[] TiffsaveBuffer(Enums.ForeignTiffCompression? compression = null, int? q = null, Enums.ForeignTiffPredictor? predictor = null, bool? tile = null, int? tileWidth = null, int? tileHeight = null, bool? pyramid = null, bool? miniswhite = null, int? bitdepth = null, Enums.ForeignTiffResunit? resunit = null, double? xres = null, double? yres = null, bool? bigtiff = null, bool? properties = null, Enums.RegionShrink? regionShrink = null, int? level = null, bool? lossless = null, Enums.ForeignDzDepth? depth = null, bool? subifd = null, bool? premultiply = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(compression), compression); - options.AddIfPresent("Q", q); - options.AddIfPresent(nameof(predictor), predictor); - options.AddIfPresent(nameof(tile), tile); - options.AddIfPresent("tile_width", tileWidth); - options.AddIfPresent("tile_height", tileHeight); - options.AddIfPresent(nameof(pyramid), pyramid); - options.AddIfPresent(nameof(miniswhite), miniswhite); - options.AddIfPresent(nameof(bitdepth), bitdepth); - options.AddIfPresent(nameof(resunit), resunit); - options.AddIfPresent(nameof(xres), xres); - options.AddIfPresent(nameof(yres), yres); - options.AddIfPresent(nameof(bigtiff), bigtiff); - options.AddIfPresent(nameof(properties), properties); - options.AddIfPresent("region_shrink", regionShrink); - options.AddIfPresent(nameof(level), level); - options.AddIfPresent(nameof(lossless), lossless); - options.AddIfPresent(nameof(depth), depth); - options.AddIfPresent(nameof(subifd), subifd); - options.AddIfPresent(nameof(premultiply), premultiply); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - return this.Call("tiffsave_buffer", options) as byte[]; - } - - /// - /// Save image to tiff target. - /// - /// - /// - /// in.TiffsaveTarget(target, compression: Enums.ForeignTiffCompression, q: int, predictor: Enums.ForeignTiffPredictor, tile: bool, tileWidth: int, tileHeight: int, pyramid: bool, miniswhite: bool, bitdepth: int, resunit: Enums.ForeignTiffResunit, xres: double, yres: double, bigtiff: bool, properties: bool, regionShrink: Enums.RegionShrink, level: int, lossless: bool, depth: Enums.ForeignDzDepth, subifd: bool, premultiply: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Target to save to. - /// Compression for this file. - /// Q factor. - /// Compression prediction. - /// Write a tiled tiff. - /// Tile width in pixels. - /// Tile height in pixels. - /// Write a pyramidal tiff. - /// Use 0 for white in 1-bit images. - /// Write as a 1, 2, 4 or 8 bit image. - /// Resolution unit. - /// Horizontal resolution in pixels/mm. - /// Vertical resolution in pixels/mm. - /// Write a bigtiff image. - /// Write a properties document to IMAGEDESCRIPTION. - /// Method to shrink regions. - /// ZSTD compression level. - /// Enable WEBP lossless mode. - /// Pyramid depth. - /// Save pyr layers as sub-IFDs. - /// Save with premultiplied alpha. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void TiffsaveTarget(Target target, Enums.ForeignTiffCompression? compression = null, int? q = null, Enums.ForeignTiffPredictor? predictor = null, bool? tile = null, int? tileWidth = null, int? tileHeight = null, bool? pyramid = null, bool? miniswhite = null, int? bitdepth = null, Enums.ForeignTiffResunit? resunit = null, double? xres = null, double? yres = null, bool? bigtiff = null, bool? properties = null, Enums.RegionShrink? regionShrink = null, int? level = null, bool? lossless = null, Enums.ForeignDzDepth? depth = null, bool? subifd = null, bool? premultiply = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(compression), compression); - options.AddIfPresent("Q", q); - options.AddIfPresent(nameof(predictor), predictor); - options.AddIfPresent(nameof(tile), tile); - options.AddIfPresent("tile_width", tileWidth); - options.AddIfPresent("tile_height", tileHeight); - options.AddIfPresent(nameof(pyramid), pyramid); - options.AddIfPresent(nameof(miniswhite), miniswhite); - options.AddIfPresent(nameof(bitdepth), bitdepth); - options.AddIfPresent(nameof(resunit), resunit); - options.AddIfPresent(nameof(xres), xres); - options.AddIfPresent(nameof(yres), yres); - options.AddIfPresent(nameof(bigtiff), bigtiff); - options.AddIfPresent(nameof(properties), properties); - options.AddIfPresent("region_shrink", regionShrink); - options.AddIfPresent(nameof(level), level); - options.AddIfPresent(nameof(lossless), lossless); - options.AddIfPresent(nameof(depth), depth); - options.AddIfPresent(nameof(subifd), subifd); - options.AddIfPresent(nameof(premultiply), premultiply); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("tiffsave_target", options, target); - } - - /// - /// Save image to tiff stream. - /// - /// - /// - /// in.TiffsaveStream(stream, compression: Enums.ForeignTiffCompression, q: int, predictor: Enums.ForeignTiffPredictor, tile: bool, tileWidth: int, tileHeight: int, pyramid: bool, miniswhite: bool, bitdepth: int, resunit: Enums.ForeignTiffResunit, xres: double, yres: double, bigtiff: bool, properties: bool, regionShrink: Enums.RegionShrink, level: int, lossless: bool, depth: Enums.ForeignDzDepth, subifd: bool, premultiply: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Stream to save to. - /// Compression for this file. - /// Q factor. - /// Compression prediction. - /// Write a tiled tiff. - /// Tile width in pixels. - /// Tile height in pixels. - /// Write a pyramidal tiff. - /// Use 0 for white in 1-bit images. - /// Write as a 1, 2, 4 or 8 bit image. - /// Resolution unit. - /// Horizontal resolution in pixels/mm. - /// Vertical resolution in pixels/mm. - /// Write a bigtiff image. - /// Write a properties document to IMAGEDESCRIPTION. - /// Method to shrink regions. - /// ZSTD compression level. - /// Enable WEBP lossless mode. - /// Pyramid depth. - /// Save pyr layers as sub-IFDs. - /// Save with premultiplied alpha. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void TiffsaveStream(Stream stream, Enums.ForeignTiffCompression? compression = null, int? q = null, Enums.ForeignTiffPredictor? predictor = null, bool? tile = null, int? tileWidth = null, int? tileHeight = null, bool? pyramid = null, bool? miniswhite = null, int? bitdepth = null, Enums.ForeignTiffResunit? resunit = null, double? xres = null, double? yres = null, bool? bigtiff = null, bool? properties = null, Enums.RegionShrink? regionShrink = null, int? level = null, bool? lossless = null, Enums.ForeignDzDepth? depth = null, bool? subifd = null, bool? premultiply = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - using var target = TargetStream.NewFromStream(stream); - TiffsaveTarget(target, compression, q, predictor, tile, tileWidth, tileHeight, pyramid, miniswhite, bitdepth, resunit, xres, yres, bigtiff, properties, regionShrink, level, lossless, depth, subifd, premultiply, keep, background, pageHeight, profile); - } - - /// - /// Cache an image as a set of tiles. - /// - /// - /// - /// using Image @out = in.Tilecache(tileWidth: int, tileHeight: int, maxTiles: int, access: Enums.Access, threaded: bool, persistent: bool); - /// - /// - /// Tile width in pixels. - /// Tile height in pixels. - /// Maximum number of tiles to cache. - /// Expected access pattern. - /// Allow threaded access. - /// Keep cache between evaluations. - /// A new . - public Image Tilecache(int? tileWidth = null, int? tileHeight = null, int? maxTiles = null, Enums.Access? access = null, bool? threaded = null, bool? persistent = null) - { - var options = new VOption(); - - options.AddIfPresent("tile_width", tileWidth); - options.AddIfPresent("tile_height", tileHeight); - options.AddIfPresent("max_tiles", maxTiles); - options.AddIfPresent(nameof(access), access); - options.AddIfPresent(nameof(threaded), threaded); - options.AddIfPresent(nameof(persistent), persistent); - - return this.Call("tilecache", options) as Image; - } - - /// - /// Build a look-up table. - /// - /// - /// - /// using Image @out = NetVips.Image.Tonelut(inMax: int, outMax: int, lb: double, lw: double, ps: double, pm: double, ph: double, s: double, m: double, h: double); - /// - /// - /// Size of LUT to build. - /// Maximum value in output LUT. - /// Lowest value in output. - /// Highest value in output. - /// Position of shadow. - /// Position of mid-tones. - /// Position of highlights. - /// Adjust shadows by this much. - /// Adjust mid-tones by this much. - /// Adjust highlights by this much. - /// A new . - public static Image Tonelut(int? inMax = null, int? outMax = null, double? lb = null, double? lw = null, double? ps = null, double? pm = null, double? ph = null, double? s = null, double? m = null, double? h = null) - { - var options = new VOption(); - - options.AddIfPresent("in_max", inMax); - options.AddIfPresent("out_max", outMax); - options.AddIfPresent("Lb", lb); - options.AddIfPresent("Lw", lw); - options.AddIfPresent("Ps", ps); - options.AddIfPresent("Pm", pm); - options.AddIfPresent("Ph", ph); - options.AddIfPresent("S", s); - options.AddIfPresent("M", m); - options.AddIfPresent("H", h); - - return Operation.Call("tonelut", options) as Image; - } - - /// - /// Transpose3d an image. - /// - /// - /// - /// using Image @out = in.Transpose3d(pageHeight: int); - /// - /// - /// Height of each input page. - /// A new . - public Image Transpose3d(int? pageHeight = null) - { - var options = new VOption(); - - options.AddIfPresent("page_height", pageHeight); - - return this.Call("transpose3d", options) as Image; - } - - /// - /// Unpremultiply image alpha. - /// - /// - /// - /// using Image @out = in.Unpremultiply(maxAlpha: double, alphaBand: int); - /// - /// - /// Maximum value of alpha channel. - /// Unpremultiply with this alpha. - /// A new . - public Image Unpremultiply(double? maxAlpha = null, int? alphaBand = null) - { - var options = new VOption(); - - options.AddIfPresent("max_alpha", maxAlpha); - options.AddIfPresent("alpha_band", alphaBand); - - return this.Call("unpremultiply", options) as Image; - } - - /// - /// Load vips from file. - /// - /// - /// - /// using Image @out = NetVips.Image.Vipsload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Vipsload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("vipsload", options, filename) as Image; - } - - /// - /// Load vips from file. - /// - /// - /// - /// using Image @out = NetVips.Image.Vipsload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Vipsload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("vipsload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load vips from source. - /// - /// - /// - /// using Image @out = NetVips.Image.VipsloadSource(source, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image VipsloadSource(Source source, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("vipsload_source", options, source) as Image; - } - - /// - /// Load vips from stream. - /// - /// - /// - /// using Image @out = NetVips.Image.VipsloadStream(stream, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image VipsloadStream(Stream stream, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = VipsloadSource(source, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load vips from source. - /// - /// - /// - /// using Image @out = NetVips.Image.VipsloadSource(source, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image VipsloadSource(Source source, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("vipsload_source", options, source) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load vips from stream. - /// - /// - /// - /// using Image @out = NetVips.Image.VipsloadStream(stream, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Flags for this file. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image VipsloadStream(Stream stream, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = VipsloadSource(source, out flags, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Save image to file in vips format. - /// - /// - /// - /// in.Vipssave(filename, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Filename to save to. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Vipssave(string filename, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("vipssave", options, filename); - } - - /// - /// Save image to target in vips format. - /// - /// - /// - /// in.VipssaveTarget(target, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Target to save to. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void VipssaveTarget(Target target, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("vipssave_target", options, target); - } - - /// - /// Save image to stream in vips format. - /// - /// - /// - /// in.VipssaveStream(stream, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Stream to save to. - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void VipssaveStream(Stream stream, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - using var target = TargetStream.NewFromStream(stream); - VipssaveTarget(target, keep, background, pageHeight, profile); - } - - /// - /// Load webp from file. - /// - /// - /// - /// using Image @out = NetVips.Image.Webpload(filename, page: int, n: int, scale: double, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Factor to scale by. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Webpload(string filename, int? page = null, int? n = null, double? scale = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("webpload", options, filename) as Image; - } - - /// - /// Load webp from file. - /// - /// - /// - /// using Image @out = NetVips.Image.Webpload(filename, out var flags, page: int, n: int, scale: double, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Filename to load from. - /// Flags for this file. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Factor to scale by. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image Webpload(string filename, out Enums.ForeignFlags flags, int? page = null, int? n = null, double? scale = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("webpload", options, filename) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load webp from buffer. - /// - /// - /// - /// using Image @out = NetVips.Image.WebploadBuffer(buffer, page: int, n: int, scale: double, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Factor to scale by. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image WebploadBuffer(byte[] buffer, int? page = null, int? n = null, double? scale = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("webpload_buffer", options, buffer) as Image; - } - - /// - /// Load webp from buffer. - /// - /// - /// - /// using Image @out = NetVips.Image.WebploadBuffer(buffer, out var flags, page: int, n: int, scale: double, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Buffer to load from. - /// Flags for this file. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Factor to scale by. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image WebploadBuffer(byte[] buffer, out Enums.ForeignFlags flags, int? page = null, int? n = null, double? scale = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("webpload_buffer", options, buffer) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load webp from source. - /// - /// - /// - /// using Image @out = NetVips.Image.WebploadSource(source, page: int, n: int, scale: double, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Factor to scale by. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image WebploadSource(Source source, int? page = null, int? n = null, double? scale = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - return Operation.Call("webpload_source", options, source) as Image; - } - - /// - /// Load webp from stream. - /// - /// - /// - /// using Image @out = NetVips.Image.WebploadStream(stream, page: int, n: int, scale: double, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Factor to scale by. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image WebploadStream(Stream stream, int? page = null, int? n = null, double? scale = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = WebploadSource(source, page, n, scale, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Load webp from source. - /// - /// - /// - /// using Image @out = NetVips.Image.WebploadSource(source, out var flags, page: int, n: int, scale: double, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Source to load from. - /// Flags for this file. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Factor to scale by. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image WebploadSource(Source source, out Enums.ForeignFlags flags, int? page = null, int? n = null, double? scale = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(page), page); - options.AddIfPresent(nameof(n), n); - options.AddIfPresent(nameof(scale), scale); - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); - options.AddIfPresent(nameof(revalidate), revalidate); - - options.Add("flags", true); - - var results = Operation.Call("webpload_source", options, source) as object[]; - var finalResult = results?[0] as Image; - var opts = results?[1] as VOption; - flags = (Enums.ForeignFlags)opts?["flags"]; - - return finalResult; - } - - /// - /// Load webp from stream. - /// - /// - /// - /// using Image @out = NetVips.Image.WebploadStream(stream, out var flags, page: int, n: int, scale: double, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); - /// - /// - /// Stream to load from. - /// Flags for this file. - /// First page to load. - /// Number of pages to load, -1 for all. - /// Factor to scale by. - /// Force open via memory. - /// Required access pattern for this file. - /// Error level to fail on. - /// Don't use a cached result for this operation. - /// A new . - public static Image WebploadStream(Stream stream, out Enums.ForeignFlags flags, int? page = null, int? n = null, double? scale = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) - { - var source = SourceStream.NewFromStream(stream); - var image = WebploadSource(source, out flags, page, n, scale, memory, access, failOn, revalidate); - - image.OnPostClose += () => source.Dispose(); - - return image; - } - - /// - /// Save as WebP. - /// - /// - /// - /// in.Webpsave(filename, q: int, lossless: bool, preset: Enums.ForeignWebpPreset, smartSubsample: bool, nearLossless: bool, alphaQ: int, minSize: bool, kmin: int, kmax: int, effort: int, mixed: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Filename to save to. - /// Q factor. - /// Enable lossless compression. - /// Preset for lossy compression. - /// Enable high quality chroma subsampling. - /// Enable preprocessing in lossless mode (uses Q). - /// Change alpha plane fidelity for lossy compression. - /// Optimise for minimum size. - /// Minimum number of frames between key frames. - /// Maximum number of frames between key frames. - /// Level of CPU effort to reduce file size. - /// Allow mixed encoding (might reduce file size). - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void Webpsave(string filename, int? q = null, bool? lossless = null, Enums.ForeignWebpPreset? preset = null, bool? smartSubsample = null, bool? nearLossless = null, int? alphaQ = null, bool? minSize = null, int? kmin = null, int? kmax = null, int? effort = null, bool? mixed = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent("Q", q); - options.AddIfPresent(nameof(lossless), lossless); - options.AddIfPresent(nameof(preset), preset); - options.AddIfPresent("smart_subsample", smartSubsample); - options.AddIfPresent("near_lossless", nearLossless); - options.AddIfPresent("alpha_q", alphaQ); - options.AddIfPresent("min_size", minSize); - options.AddIfPresent(nameof(kmin), kmin); - options.AddIfPresent(nameof(kmax), kmax); - options.AddIfPresent(nameof(effort), effort); - options.AddIfPresent(nameof(mixed), mixed); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("webpsave", options, filename); - } - - /// - /// Save as WebP. - /// - /// - /// - /// byte[] buffer = in.WebpsaveBuffer(q: int, lossless: bool, preset: Enums.ForeignWebpPreset, smartSubsample: bool, nearLossless: bool, alphaQ: int, minSize: bool, kmin: int, kmax: int, effort: int, mixed: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Q factor. - /// Enable lossless compression. - /// Preset for lossy compression. - /// Enable high quality chroma subsampling. - /// Enable preprocessing in lossless mode (uses Q). - /// Change alpha plane fidelity for lossy compression. - /// Optimise for minimum size. - /// Minimum number of frames between key frames. - /// Maximum number of frames between key frames. - /// Level of CPU effort to reduce file size. - /// Allow mixed encoding (might reduce file size). - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - /// An array of bytes. - public byte[] WebpsaveBuffer(int? q = null, bool? lossless = null, Enums.ForeignWebpPreset? preset = null, bool? smartSubsample = null, bool? nearLossless = null, int? alphaQ = null, bool? minSize = null, int? kmin = null, int? kmax = null, int? effort = null, bool? mixed = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent("Q", q); - options.AddIfPresent(nameof(lossless), lossless); - options.AddIfPresent(nameof(preset), preset); - options.AddIfPresent("smart_subsample", smartSubsample); - options.AddIfPresent("near_lossless", nearLossless); - options.AddIfPresent("alpha_q", alphaQ); - options.AddIfPresent("min_size", minSize); - options.AddIfPresent(nameof(kmin), kmin); - options.AddIfPresent(nameof(kmax), kmax); - options.AddIfPresent(nameof(effort), effort); - options.AddIfPresent(nameof(mixed), mixed); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - return this.Call("webpsave_buffer", options) as byte[]; - } - - /// - /// Save image to webp mime. - /// - /// - /// - /// in.WebpsaveMime(q: int, lossless: bool, preset: Enums.ForeignWebpPreset, smartSubsample: bool, nearLossless: bool, alphaQ: int, minSize: bool, kmin: int, kmax: int, effort: int, mixed: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Q factor. - /// Enable lossless compression. - /// Preset for lossy compression. - /// Enable high quality chroma subsampling. - /// Enable preprocessing in lossless mode (uses Q). - /// Change alpha plane fidelity for lossy compression. - /// Optimise for minimum size. - /// Minimum number of frames between key frames. - /// Maximum number of frames between key frames. - /// Level of CPU effort to reduce file size. - /// Allow mixed encoding (might reduce file size). - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void WebpsaveMime(int? q = null, bool? lossless = null, Enums.ForeignWebpPreset? preset = null, bool? smartSubsample = null, bool? nearLossless = null, int? alphaQ = null, bool? minSize = null, int? kmin = null, int? kmax = null, int? effort = null, bool? mixed = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent("Q", q); - options.AddIfPresent(nameof(lossless), lossless); - options.AddIfPresent(nameof(preset), preset); - options.AddIfPresent("smart_subsample", smartSubsample); - options.AddIfPresent("near_lossless", nearLossless); - options.AddIfPresent("alpha_q", alphaQ); - options.AddIfPresent("min_size", minSize); - options.AddIfPresent(nameof(kmin), kmin); - options.AddIfPresent(nameof(kmax), kmax); - options.AddIfPresent(nameof(effort), effort); - options.AddIfPresent(nameof(mixed), mixed); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("webpsave_mime", options); - } - - /// - /// Save as WebP. - /// - /// - /// - /// in.WebpsaveTarget(target, q: int, lossless: bool, preset: Enums.ForeignWebpPreset, smartSubsample: bool, nearLossless: bool, alphaQ: int, minSize: bool, kmin: int, kmax: int, effort: int, mixed: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Target to save to. - /// Q factor. - /// Enable lossless compression. - /// Preset for lossy compression. - /// Enable high quality chroma subsampling. - /// Enable preprocessing in lossless mode (uses Q). - /// Change alpha plane fidelity for lossy compression. - /// Optimise for minimum size. - /// Minimum number of frames between key frames. - /// Maximum number of frames between key frames. - /// Level of CPU effort to reduce file size. - /// Allow mixed encoding (might reduce file size). - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void WebpsaveTarget(Target target, int? q = null, bool? lossless = null, Enums.ForeignWebpPreset? preset = null, bool? smartSubsample = null, bool? nearLossless = null, int? alphaQ = null, bool? minSize = null, int? kmin = null, int? kmax = null, int? effort = null, bool? mixed = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - var options = new VOption(); - - options.AddIfPresent("Q", q); - options.AddIfPresent(nameof(lossless), lossless); - options.AddIfPresent(nameof(preset), preset); - options.AddIfPresent("smart_subsample", smartSubsample); - options.AddIfPresent("near_lossless", nearLossless); - options.AddIfPresent("alpha_q", alphaQ); - options.AddIfPresent("min_size", minSize); - options.AddIfPresent(nameof(kmin), kmin); - options.AddIfPresent(nameof(kmax), kmax); - options.AddIfPresent(nameof(effort), effort); - options.AddIfPresent(nameof(mixed), mixed); - options.AddForeignKeep(keep); - options.AddIfPresent(nameof(background), background); - options.AddIfPresent("page_height", pageHeight); - options.AddIfPresent(nameof(profile), profile); - - this.Call("webpsave_target", options, target); - } - - /// - /// Save as WebP. - /// - /// - /// - /// in.WebpsaveStream(stream, q: int, lossless: bool, preset: Enums.ForeignWebpPreset, smartSubsample: bool, nearLossless: bool, alphaQ: int, minSize: bool, kmin: int, kmax: int, effort: int, mixed: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); - /// - /// - /// Stream to save to. - /// Q factor. - /// Enable lossless compression. - /// Preset for lossy compression. - /// Enable high quality chroma subsampling. - /// Enable preprocessing in lossless mode (uses Q). - /// Change alpha plane fidelity for lossy compression. - /// Optimise for minimum size. - /// Minimum number of frames between key frames. - /// Maximum number of frames between key frames. - /// Level of CPU effort to reduce file size. - /// Allow mixed encoding (might reduce file size). - /// Which metadata to retain. - /// Background value. - /// Set page height for multipage save. - /// Filename of ICC profile to embed. - public void WebpsaveStream(Stream stream, int? q = null, bool? lossless = null, Enums.ForeignWebpPreset? preset = null, bool? smartSubsample = null, bool? nearLossless = null, int? alphaQ = null, bool? minSize = null, int? kmin = null, int? kmax = null, int? effort = null, bool? mixed = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) - { - using var target = TargetStream.NewFromStream(stream); - WebpsaveTarget(target, q, lossless, preset, smartSubsample, nearLossless, alphaQ, minSize, kmin, kmax, effort, mixed, keep, background, pageHeight, profile); - } - - /// - /// Make a worley noise image. - /// - /// - /// - /// using Image @out = NetVips.Image.Worley(width, height, cellSize: int, seed: int); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Size of Worley cells. - /// Random number seed. - /// A new . - public static Image Worley(int width, int height, int? cellSize = null, int? seed = null) - { - var options = new VOption(); - - options.AddIfPresent("cell_size", cellSize); - options.AddIfPresent(nameof(seed), seed); - - return Operation.Call("worley", options, width, height) as Image; - } - - /// - /// Wrap image origin. - /// - /// - /// - /// using Image @out = in.Wrap(x: int, y: int); - /// - /// - /// Left edge of input in output. - /// Top edge of input in output. - /// A new . - public Image Wrap(int? x = null, int? y = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(x), x); - options.AddIfPresent(nameof(y), y); - - return this.Call("wrap", options) as Image; - } - - /// - /// Make an image where pixel values are coordinates. - /// - /// - /// - /// using Image @out = NetVips.Image.Xyz(width, height, csize: int, dsize: int, esize: int); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Size of third dimension. - /// Size of fourth dimension. - /// Size of fifth dimension. - /// A new . - public static Image Xyz(int width, int height, int? csize = null, int? dsize = null, int? esize = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(csize), csize); - options.AddIfPresent(nameof(dsize), dsize); - options.AddIfPresent(nameof(esize), esize); - - return Operation.Call("xyz", options, width, height) as Image; - } - - /// - /// Transform XYZ to CMYK. - /// - /// - /// - /// using Image @out = in.XYZ2CMYK(); - /// - /// - /// A new . - public Image XYZ2CMYK() - { - return this.Call("XYZ2CMYK") as Image; - } - - /// - /// Transform XYZ to Lab. - /// - /// - /// - /// using Image @out = in.XYZ2Lab(temp: double[]); - /// - /// - /// Colour temperature. - /// A new . - public Image XYZ2Lab(double[] temp = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(temp), temp); - - return this.Call("XYZ2Lab", options) as Image; - } - - /// - /// Transform XYZ to scRGB. - /// - /// - /// - /// using Image @out = in.XYZ2scRGB(); - /// - /// - /// A new . - public Image XYZ2scRGB() - { - return this.Call("XYZ2scRGB") as Image; - } - - /// - /// Transform XYZ to Yxy. - /// - /// - /// - /// using Image @out = in.XYZ2Yxy(); - /// - /// - /// A new . - public Image XYZ2Yxy() - { - return this.Call("XYZ2Yxy") as Image; - } - - /// - /// Transform Yxy to XYZ. - /// - /// - /// - /// using Image @out = in.Yxy2XYZ(); - /// - /// - /// A new . - public Image Yxy2XYZ() - { - return this.Call("Yxy2XYZ") as Image; - } - - /// - /// Make a zone plate. - /// - /// - /// - /// using Image @out = NetVips.Image.Zone(width, height, uchar: bool); - /// - /// - /// Image width in pixels. - /// Image height in pixels. - /// Output an unsigned char image. - /// A new . - public static Image Zone(int width, int height, bool? uchar = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(uchar), uchar); - - return Operation.Call("zone", options, width, height) as Image; - } - - /// - /// Zoom an image. - /// - /// - /// - /// using Image @out = input.Zoom(xfac, yfac); - /// - /// - /// Horizontal zoom factor. - /// Vertical zoom factor. - /// A new . - public Image Zoom(int xfac, int yfac) + {"distance", true} + }; + + var results = this.Call("fill_nearest", optionalOutput) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + distance = opts?["distance"] as Image; + + return finalResult; + } + + /// + /// Search an image for non-edge areas. + /// + /// + /// + /// var output = in.FindTrim(threshold: double, background: double[], lineArt: bool); + /// + /// + /// Object threshold. + /// Color for background pixels. + /// Enable line art mode. + /// An array of objects. + public object[] FindTrim(double? threshold = null, double[] background = null, bool? lineArt = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(threshold), threshold); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("line_art", lineArt); + + return this.Call("find_trim", options) as object[]; + } + + /// + /// Load a FITS image. + /// + /// + /// + /// using Image @out = NetVips.Image.Fitsload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Fitsload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("fitsload", options, filename) as Image; + } + + /// + /// Load a FITS image. + /// + /// + /// + /// using Image @out = NetVips.Image.Fitsload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Fitsload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("fitsload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load FITS from a source. + /// + /// + /// + /// using Image @out = NetVips.Image.FitsloadSource(source, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image FitsloadSource(Source source, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("fitsload_source", options, source) as Image; + } + + /// + /// Load FITS from a stream. + /// + /// + /// + /// using Image @out = NetVips.Image.FitsloadStream(stream, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image FitsloadStream(Stream stream, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = FitsloadSource(source, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load FITS from a source. + /// + /// + /// + /// using Image @out = NetVips.Image.FitsloadSource(source, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image FitsloadSource(Source source, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("fitsload_source", options, source) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load FITS from a stream. + /// + /// + /// + /// using Image @out = NetVips.Image.FitsloadStream(stream, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image FitsloadStream(Stream stream, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = FitsloadSource(source, out flags, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Save image to fits file. + /// + /// + /// + /// in.Fitssave(filename, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Filename to save to. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Fitssave(string filename, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("fitssave", options, filename); + } + + /// + /// Flatten alpha out of an image. + /// + /// + /// + /// using Image @out = in.Flatten(background: double[], maxAlpha: double); + /// + /// + /// Background value. + /// Maximum value of alpha channel. + /// A new . + public Image Flatten(double[] background = null, double? maxAlpha = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("max_alpha", maxAlpha); + + return this.Call("flatten", options) as Image; + } + + /// + /// Flip an image. + /// + /// + /// + /// using Image @out = in.Flip(direction); + /// + /// + /// Direction to flip image. + /// A new . + public Image Flip(Enums.Direction direction) + { + return this.Call("flip", direction) as Image; + } + + /// + /// Transform float RGB to Radiance coding. + /// + /// + /// + /// using Image @out = in.Float2rad(); + /// + /// + /// A new . + public Image Float2rad() + { + return this.Call("float2rad") as Image; + } + + /// + /// Make a fractal surface. + /// + /// + /// + /// using Image @out = NetVips.Image.Fractsurf(width, height, fractalDimension); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Fractal dimension. + /// A new . + public static Image Fractsurf(int width, int height, double fractalDimension) + { + return Operation.Call("fractsurf", width, height, fractalDimension) as Image; + } + + /// + /// Frequency-domain filtering. + /// + /// + /// + /// using Image @out = in.Freqmult(mask); + /// + /// + /// Input mask image. + /// A new . + public Image Freqmult(Image mask) + { + return this.Call("freqmult", mask) as Image; + } + + /// + /// Forward FFT. + /// + /// + /// + /// using Image @out = in.Fwfft(); + /// + /// + /// A new . + public Image Fwfft() + { + return this.Call("fwfft") as Image; + } + + /// + /// Gamma an image. + /// + /// + /// + /// using Image @out = in.Gamma(exponent: double); + /// + /// + /// Gamma factor. + /// A new . + public Image Gamma(double? exponent = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(exponent), exponent); + + return this.Call("gamma", options) as Image; + } + + /// + /// Gaussian blur. + /// + /// + /// + /// using Image @out = in.Gaussblur(sigma, minAmpl: double, precision: Enums.Precision); + /// + /// + /// Sigma of Gaussian. + /// Minimum amplitude of Gaussian. + /// Convolve with this precision. + /// A new . + public Image Gaussblur(double sigma, double? minAmpl = null, Enums.Precision? precision = null) + { + var options = new VOption(); + + options.AddIfPresent("min_ampl", minAmpl); + options.AddIfPresent(nameof(precision), precision); + + return this.Call("gaussblur", options, sigma) as Image; + } + + /// + /// Make a gaussian image. + /// + /// + /// + /// using Image @out = NetVips.Image.Gaussmat(sigma, minAmpl, separable: bool, precision: Enums.Precision); + /// + /// + /// Sigma of Gaussian. + /// Minimum amplitude of Gaussian. + /// Generate separable Gaussian. + /// Generate with this precision. + /// A new . + public static Image Gaussmat(double sigma, double minAmpl, bool? separable = null, Enums.Precision? precision = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(separable), separable); + options.AddIfPresent(nameof(precision), precision); + + return Operation.Call("gaussmat", options, sigma, minAmpl) as Image; + } + + /// + /// Make a gaussnoise image. + /// + /// + /// + /// using Image @out = NetVips.Image.Gaussnoise(width, height, sigma: double, mean: double, seed: int); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Standard deviation of pixels in generated image. + /// Mean of pixels in generated image. + /// Random number seed. + /// A new . + public static Image Gaussnoise(int width, int height, double? sigma = null, double? mean = null, int? seed = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(sigma), sigma); + options.AddIfPresent(nameof(mean), mean); + options.AddIfPresent(nameof(seed), seed); + + return Operation.Call("gaussnoise", options, width, height) as Image; + } + + /// + /// Read a point from an image. + /// + /// + /// + /// double[] outArray = in.Getpoint(x, y); + /// + /// + /// Point to read. + /// Point to read. + /// An array of doubles. + public double[] Getpoint(int x, int y) + { + return this.Call("getpoint", x, y) as double[]; + } + + /// + /// Load GIF with libnsgif. + /// + /// + /// + /// using Image @out = NetVips.Image.Gifload(filename, n: int, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Number of pages to load, -1 for all. + /// First page to load. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Gifload(string filename, int? n = null, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("gifload", options, filename) as Image; + } + + /// + /// Load GIF with libnsgif. + /// + /// + /// + /// using Image @out = NetVips.Image.Gifload(filename, out var flags, n: int, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// Number of pages to load, -1 for all. + /// First page to load. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Gifload(string filename, out Enums.ForeignFlags flags, int? n = null, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("gifload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load GIF with libnsgif. + /// + /// + /// + /// using Image @out = NetVips.Image.GifloadBuffer(buffer, n: int, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Number of pages to load, -1 for all. + /// First page to load. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image GifloadBuffer(byte[] buffer, int? n = null, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("gifload_buffer", options, buffer) as Image; + } + + /// + /// Load GIF with libnsgif. + /// + /// + /// + /// using Image @out = NetVips.Image.GifloadBuffer(buffer, out var flags, n: int, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Flags for this file. + /// Number of pages to load, -1 for all. + /// First page to load. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image GifloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, int? n = null, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("gifload_buffer", options, buffer) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load gif from source. + /// + /// + /// + /// using Image @out = NetVips.Image.GifloadSource(source, n: int, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Number of pages to load, -1 for all. + /// First page to load. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image GifloadSource(Source source, int? n = null, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("gifload_source", options, source) as Image; + } + + /// + /// Load gif from stream. + /// + /// + /// + /// using Image @out = NetVips.Image.GifloadStream(stream, n: int, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Number of pages to load, -1 for all. + /// First page to load. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image GifloadStream(Stream stream, int? n = null, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = GifloadSource(source, n, page, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load gif from source. + /// + /// + /// + /// using Image @out = NetVips.Image.GifloadSource(source, out var flags, n: int, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Flags for this file. + /// Number of pages to load, -1 for all. + /// First page to load. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image GifloadSource(Source source, out Enums.ForeignFlags flags, int? n = null, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("gifload_source", options, source) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load gif from stream. + /// + /// + /// + /// using Image @out = NetVips.Image.GifloadStream(stream, out var flags, n: int, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Flags for this file. + /// Number of pages to load, -1 for all. + /// First page to load. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image GifloadStream(Stream stream, out Enums.ForeignFlags flags, int? n = null, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = GifloadSource(source, out flags, n, page, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Save as gif. + /// + /// + /// + /// in.Gifsave(filename, dither: double, effort: int, bitdepth: int, interframeMaxerror: double, reuse: bool, interpaletteMaxerror: double, interlace: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Filename to save to. + /// Amount of dithering. + /// Quantisation effort. + /// Number of bits per pixel. + /// Maximum inter-frame error for transparency. + /// Reuse palette from input. + /// Maximum inter-palette error for palette reusage. + /// Generate an interlaced (progressive) GIF. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Gifsave(string filename, double? dither = null, int? effort = null, int? bitdepth = null, double? interframeMaxerror = null, bool? reuse = null, double? interpaletteMaxerror = null, bool? interlace = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(dither), dither); + options.AddIfPresent(nameof(effort), effort); + options.AddIfPresent(nameof(bitdepth), bitdepth); + options.AddIfPresent("interframe_maxerror", interframeMaxerror); + options.AddIfPresent(nameof(reuse), reuse); + options.AddIfPresent("interpalette_maxerror", interpaletteMaxerror); + options.AddIfPresent(nameof(interlace), interlace); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("gifsave", options, filename); + } + + /// + /// Save as gif. + /// + /// + /// + /// byte[] buffer = in.GifsaveBuffer(dither: double, effort: int, bitdepth: int, interframeMaxerror: double, reuse: bool, interpaletteMaxerror: double, interlace: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Amount of dithering. + /// Quantisation effort. + /// Number of bits per pixel. + /// Maximum inter-frame error for transparency. + /// Reuse palette from input. + /// Maximum inter-palette error for palette reusage. + /// Generate an interlaced (progressive) GIF. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + /// An array of bytes. + public byte[] GifsaveBuffer(double? dither = null, int? effort = null, int? bitdepth = null, double? interframeMaxerror = null, bool? reuse = null, double? interpaletteMaxerror = null, bool? interlace = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(dither), dither); + options.AddIfPresent(nameof(effort), effort); + options.AddIfPresent(nameof(bitdepth), bitdepth); + options.AddIfPresent("interframe_maxerror", interframeMaxerror); + options.AddIfPresent(nameof(reuse), reuse); + options.AddIfPresent("interpalette_maxerror", interpaletteMaxerror); + options.AddIfPresent(nameof(interlace), interlace); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + return this.Call("gifsave_buffer", options) as byte[]; + } + + /// + /// Save as gif. + /// + /// + /// + /// in.GifsaveTarget(target, dither: double, effort: int, bitdepth: int, interframeMaxerror: double, reuse: bool, interpaletteMaxerror: double, interlace: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Target to save to. + /// Amount of dithering. + /// Quantisation effort. + /// Number of bits per pixel. + /// Maximum inter-frame error for transparency. + /// Reuse palette from input. + /// Maximum inter-palette error for palette reusage. + /// Generate an interlaced (progressive) GIF. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void GifsaveTarget(Target target, double? dither = null, int? effort = null, int? bitdepth = null, double? interframeMaxerror = null, bool? reuse = null, double? interpaletteMaxerror = null, bool? interlace = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(dither), dither); + options.AddIfPresent(nameof(effort), effort); + options.AddIfPresent(nameof(bitdepth), bitdepth); + options.AddIfPresent("interframe_maxerror", interframeMaxerror); + options.AddIfPresent(nameof(reuse), reuse); + options.AddIfPresent("interpalette_maxerror", interpaletteMaxerror); + options.AddIfPresent(nameof(interlace), interlace); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("gifsave_target", options, target); + } + + /// + /// Save as gif. + /// + /// + /// + /// in.GifsaveStream(stream, dither: double, effort: int, bitdepth: int, interframeMaxerror: double, reuse: bool, interpaletteMaxerror: double, interlace: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Stream to save to. + /// Amount of dithering. + /// Quantisation effort. + /// Number of bits per pixel. + /// Maximum inter-frame error for transparency. + /// Reuse palette from input. + /// Maximum inter-palette error for palette reusage. + /// Generate an interlaced (progressive) GIF. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void GifsaveStream(Stream stream, double? dither = null, int? effort = null, int? bitdepth = null, double? interframeMaxerror = null, bool? reuse = null, double? interpaletteMaxerror = null, bool? interlace = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + using var target = TargetStream.NewFromStream(stream); + GifsaveTarget(target, dither, effort, bitdepth, interframeMaxerror, reuse, interpaletteMaxerror, interlace, keep, background, pageHeight, profile); + } + + /// + /// Global balance an image mosaic. + /// + /// + /// + /// using Image @out = in.Globalbalance(gamma: double, intOutput: bool); + /// + /// + /// Image gamma. + /// Integer output. + /// A new . + public Image Globalbalance(double? gamma = null, bool? intOutput = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(gamma), gamma); + options.AddIfPresent("int_output", intOutput); + + return this.Call("globalbalance", options) as Image; + } + + /// + /// Place an image within a larger image with a certain gravity. + /// + /// + /// + /// using Image @out = in.Gravity(direction, width, height, extend: Enums.Extend, background: double[]); + /// + /// + /// Direction to place image within width/height. + /// Image width in pixels. + /// Image height in pixels. + /// How to generate the extra pixels. + /// Color for background pixels. + /// A new . + public Image Gravity(Enums.CompassDirection direction, int width, int height, Enums.Extend? extend = null, double[] background = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(extend), extend); + options.AddIfPresent(nameof(background), background); + + return this.Call("gravity", options, direction, width, height) as Image; + } + + /// + /// Make a grey ramp image. + /// + /// + /// + /// using Image @out = NetVips.Image.Grey(width, height, uchar: bool); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Output an unsigned char image. + /// A new . + public static Image Grey(int width, int height, bool? uchar = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(uchar), uchar); + + return Operation.Call("grey", options, width, height) as Image; + } + + /// + /// Grid an image. + /// + /// + /// + /// using Image @out = in.Grid(tileHeight, across, down); + /// + /// + /// Chop into tiles this high. + /// Number of tiles across. + /// Number of tiles down. + /// A new . + public Image Grid(int tileHeight, int across, int down) + { + return this.Call("grid", tileHeight, across, down) as Image; + } + + /// + /// Load a HEIF image. + /// + /// + /// + /// using Image @out = NetVips.Image.Heifload(filename, page: int, n: int, thumbnail: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Fetch thumbnail image. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Heifload(string filename, int? page = null, int? n = null, bool? thumbnail = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(thumbnail), thumbnail); + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("heifload", options, filename) as Image; + } + + /// + /// Load a HEIF image. + /// + /// + /// + /// using Image @out = NetVips.Image.Heifload(filename, out var flags, page: int, n: int, thumbnail: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Fetch thumbnail image. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Heifload(string filename, out Enums.ForeignFlags flags, int? page = null, int? n = null, bool? thumbnail = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(thumbnail), thumbnail); + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("heifload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load a HEIF image. + /// + /// + /// + /// using Image @out = NetVips.Image.HeifloadBuffer(buffer, page: int, n: int, thumbnail: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Fetch thumbnail image. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image HeifloadBuffer(byte[] buffer, int? page = null, int? n = null, bool? thumbnail = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(thumbnail), thumbnail); + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("heifload_buffer", options, buffer) as Image; + } + + /// + /// Load a HEIF image. + /// + /// + /// + /// using Image @out = NetVips.Image.HeifloadBuffer(buffer, out var flags, page: int, n: int, thumbnail: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Flags for this file. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Fetch thumbnail image. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image HeifloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, int? page = null, int? n = null, bool? thumbnail = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(thumbnail), thumbnail); + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("heifload_buffer", options, buffer) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load a HEIF image. + /// + /// + /// + /// using Image @out = NetVips.Image.HeifloadSource(source, page: int, n: int, thumbnail: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Fetch thumbnail image. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image HeifloadSource(Source source, int? page = null, int? n = null, bool? thumbnail = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(thumbnail), thumbnail); + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("heifload_source", options, source) as Image; + } + + /// + /// Load a HEIF image. + /// + /// + /// + /// using Image @out = NetVips.Image.HeifloadStream(stream, page: int, n: int, thumbnail: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Fetch thumbnail image. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image HeifloadStream(Stream stream, int? page = null, int? n = null, bool? thumbnail = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = HeifloadSource(source, page, n, thumbnail, unlimited, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load a HEIF image. + /// + /// + /// + /// using Image @out = NetVips.Image.HeifloadSource(source, out var flags, page: int, n: int, thumbnail: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Flags for this file. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Fetch thumbnail image. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image HeifloadSource(Source source, out Enums.ForeignFlags flags, int? page = null, int? n = null, bool? thumbnail = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(thumbnail), thumbnail); + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("heifload_source", options, source) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load a HEIF image. + /// + /// + /// + /// using Image @out = NetVips.Image.HeifloadStream(stream, out var flags, page: int, n: int, thumbnail: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Flags for this file. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Fetch thumbnail image. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image HeifloadStream(Stream stream, out Enums.ForeignFlags flags, int? page = null, int? n = null, bool? thumbnail = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = HeifloadSource(source, out flags, page, n, thumbnail, unlimited, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Save image in HEIF format. + /// + /// + /// + /// in.Heifsave(filename, q: int, bitdepth: int, lossless: bool, compression: Enums.ForeignHeifCompression, effort: int, subsampleMode: Enums.ForeignSubsample, encoder: Enums.ForeignHeifEncoder, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Filename to save to. + /// Q factor. + /// Number of bits per pixel. + /// Enable lossless compression. + /// Compression format. + /// CPU effort. + /// Select chroma subsample operation mode. + /// Select encoder to use. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Heifsave(string filename, int? q = null, int? bitdepth = null, bool? lossless = null, Enums.ForeignHeifCompression? compression = null, int? effort = null, Enums.ForeignSubsample? subsampleMode = null, Enums.ForeignHeifEncoder? encoder = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent("Q", q); + options.AddIfPresent(nameof(bitdepth), bitdepth); + options.AddIfPresent(nameof(lossless), lossless); + options.AddIfPresent(nameof(compression), compression); + options.AddIfPresent(nameof(effort), effort); + options.AddIfPresent("subsample_mode", subsampleMode); + options.AddIfPresent(nameof(encoder), encoder); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("heifsave", options, filename); + } + + /// + /// Save image in HEIF format. + /// + /// + /// + /// byte[] buffer = in.HeifsaveBuffer(q: int, bitdepth: int, lossless: bool, compression: Enums.ForeignHeifCompression, effort: int, subsampleMode: Enums.ForeignSubsample, encoder: Enums.ForeignHeifEncoder, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Q factor. + /// Number of bits per pixel. + /// Enable lossless compression. + /// Compression format. + /// CPU effort. + /// Select chroma subsample operation mode. + /// Select encoder to use. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + /// An array of bytes. + public byte[] HeifsaveBuffer(int? q = null, int? bitdepth = null, bool? lossless = null, Enums.ForeignHeifCompression? compression = null, int? effort = null, Enums.ForeignSubsample? subsampleMode = null, Enums.ForeignHeifEncoder? encoder = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent("Q", q); + options.AddIfPresent(nameof(bitdepth), bitdepth); + options.AddIfPresent(nameof(lossless), lossless); + options.AddIfPresent(nameof(compression), compression); + options.AddIfPresent(nameof(effort), effort); + options.AddIfPresent("subsample_mode", subsampleMode); + options.AddIfPresent(nameof(encoder), encoder); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + return this.Call("heifsave_buffer", options) as byte[]; + } + + /// + /// Save image in HEIF format. + /// + /// + /// + /// in.HeifsaveTarget(target, q: int, bitdepth: int, lossless: bool, compression: Enums.ForeignHeifCompression, effort: int, subsampleMode: Enums.ForeignSubsample, encoder: Enums.ForeignHeifEncoder, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Target to save to. + /// Q factor. + /// Number of bits per pixel. + /// Enable lossless compression. + /// Compression format. + /// CPU effort. + /// Select chroma subsample operation mode. + /// Select encoder to use. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void HeifsaveTarget(Target target, int? q = null, int? bitdepth = null, bool? lossless = null, Enums.ForeignHeifCompression? compression = null, int? effort = null, Enums.ForeignSubsample? subsampleMode = null, Enums.ForeignHeifEncoder? encoder = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent("Q", q); + options.AddIfPresent(nameof(bitdepth), bitdepth); + options.AddIfPresent(nameof(lossless), lossless); + options.AddIfPresent(nameof(compression), compression); + options.AddIfPresent(nameof(effort), effort); + options.AddIfPresent("subsample_mode", subsampleMode); + options.AddIfPresent(nameof(encoder), encoder); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("heifsave_target", options, target); + } + + /// + /// Save image in HEIF format. + /// + /// + /// + /// in.HeifsaveStream(stream, q: int, bitdepth: int, lossless: bool, compression: Enums.ForeignHeifCompression, effort: int, subsampleMode: Enums.ForeignSubsample, encoder: Enums.ForeignHeifEncoder, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Stream to save to. + /// Q factor. + /// Number of bits per pixel. + /// Enable lossless compression. + /// Compression format. + /// CPU effort. + /// Select chroma subsample operation mode. + /// Select encoder to use. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void HeifsaveStream(Stream stream, int? q = null, int? bitdepth = null, bool? lossless = null, Enums.ForeignHeifCompression? compression = null, int? effort = null, Enums.ForeignSubsample? subsampleMode = null, Enums.ForeignHeifEncoder? encoder = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + using var target = TargetStream.NewFromStream(stream); + HeifsaveTarget(target, q, bitdepth, lossless, compression, effort, subsampleMode, encoder, keep, background, pageHeight, profile); + } + + /// + /// Form cumulative histogram. + /// + /// + /// + /// using Image @out = in.HistCum(); + /// + /// + /// A new . + public Image HistCum() + { + return this.Call("hist_cum") as Image; + } + + /// + /// Estimate image entropy. + /// + /// + /// + /// double @out = in.HistEntropy(); + /// + /// + /// A double. + public double HistEntropy() + { + return this.Call("hist_entropy") is double result ? result : 0d; + } + + /// + /// Histogram equalisation. + /// + /// + /// + /// using Image @out = in.HistEqual(band: int); + /// + /// + /// Equalise with this band. + /// A new . + public Image HistEqual(int? band = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(band), band); + + return this.Call("hist_equal", options) as Image; + } + + /// + /// Find image histogram. + /// + /// + /// + /// using Image @out = in.HistFind(band: int); + /// + /// + /// Find histogram of band. + /// A new . + public Image HistFind(int? band = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(band), band); + + return this.Call("hist_find", options) as Image; + } + + /// + /// Find indexed image histogram. + /// + /// + /// + /// using Image @out = in.HistFindIndexed(index, combine: Enums.Combine); + /// + /// + /// Index image. + /// Combine bins like this. + /// A new . + public Image HistFindIndexed(Image index, Enums.Combine? combine = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(combine), combine); + + return this.Call("hist_find_indexed", options, index) as Image; + } + + /// + /// Find n-dimensional image histogram. + /// + /// + /// + /// using Image @out = in.HistFindNdim(bins: int); + /// + /// + /// Number of bins in each dimension. + /// A new . + public Image HistFindNdim(int? bins = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(bins), bins); + + return this.Call("hist_find_ndim", options) as Image; + } + + /// + /// Test for monotonicity. + /// + /// + /// + /// bool monotonic = in.HistIsmonotonic(); + /// + /// + /// A bool. + public bool HistIsmonotonic() + { + return this.Call("hist_ismonotonic") is bool result && result; + } + + /// + /// Local histogram equalisation. + /// + /// + /// + /// using Image @out = in.HistLocal(width, height, maxSlope: int); + /// + /// + /// Window width in pixels. + /// Window height in pixels. + /// Maximum slope (CLAHE). + /// A new . + public Image HistLocal(int width, int height, int? maxSlope = null) + { + var options = new VOption(); + + options.AddIfPresent("max_slope", maxSlope); + + return this.Call("hist_local", options, width, height) as Image; + } + + /// + /// Match two histograms. + /// + /// + /// + /// using Image @out = in.HistMatch(@ref); + /// + /// + /// Reference histogram. + /// A new . + public Image HistMatch(Image @ref) + { + return this.Call("hist_match", @ref) as Image; + } + + /// + /// Normalise histogram. + /// + /// + /// + /// using Image @out = in.HistNorm(); + /// + /// + /// A new . + public Image HistNorm() + { + return this.Call("hist_norm") as Image; + } + + /// + /// Plot histogram. + /// + /// + /// + /// using Image @out = in.HistPlot(); + /// + /// + /// A new . + public Image HistPlot() + { + return this.Call("hist_plot") as Image; + } + + /// + /// Find hough circle transform. + /// + /// + /// + /// using Image @out = in.HoughCircle(scale: int, minRadius: int, maxRadius: int); + /// + /// + /// Scale down dimensions by this factor. + /// Smallest radius to search for. + /// Largest radius to search for. + /// A new . + public Image HoughCircle(int? scale = null, int? minRadius = null, int? maxRadius = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent("min_radius", minRadius); + options.AddIfPresent("max_radius", maxRadius); + + return this.Call("hough_circle", options) as Image; + } + + /// + /// Find hough line transform. + /// + /// + /// + /// using Image @out = in.HoughLine(width: int, height: int); + /// + /// + /// Horizontal size of parameter space. + /// Vertical size of parameter space. + /// A new . + public Image HoughLine(int? width = null, int? height = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(width), width); + options.AddIfPresent(nameof(height), height); + + return this.Call("hough_line", options) as Image; + } + + /// + /// Transform HSV to sRGB. + /// + /// + /// + /// using Image @out = in.HSV2sRGB(); + /// + /// + /// A new . + public Image HSV2sRGB() + { + return this.Call("HSV2sRGB") as Image; + } + + /// + /// Output to device with ICC profile. + /// + /// + /// + /// using Image @out = in.IccExport(pcs: Enums.PCS, intent: Enums.Intent, blackPointCompensation: bool, outputProfile: string, depth: int); + /// + /// + /// Set Profile Connection Space. + /// Rendering intent. + /// Enable black point compensation. + /// Filename to load output profile from. + /// Output device space depth in bits. + /// A new . + public Image IccExport(Enums.PCS? pcs = null, Enums.Intent? intent = null, bool? blackPointCompensation = null, string outputProfile = null, int? depth = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(pcs), pcs); + options.AddIfPresent(nameof(intent), intent); + options.AddIfPresent("black_point_compensation", blackPointCompensation); + options.AddIfPresent("output_profile", outputProfile); + options.AddIfPresent(nameof(depth), depth); + + return this.Call("icc_export", options) as Image; + } + + /// + /// Import from device with ICC profile. + /// + /// + /// + /// using Image @out = in.IccImport(pcs: Enums.PCS, intent: Enums.Intent, blackPointCompensation: bool, embedded: bool, inputProfile: string); + /// + /// + /// Set Profile Connection Space. + /// Rendering intent. + /// Enable black point compensation. + /// Use embedded input profile, if available. + /// Filename to load input profile from. + /// A new . + public Image IccImport(Enums.PCS? pcs = null, Enums.Intent? intent = null, bool? blackPointCompensation = null, bool? embedded = null, string inputProfile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(pcs), pcs); + options.AddIfPresent(nameof(intent), intent); + options.AddIfPresent("black_point_compensation", blackPointCompensation); + options.AddIfPresent(nameof(embedded), embedded); + options.AddIfPresent("input_profile", inputProfile); + + return this.Call("icc_import", options) as Image; + } + + /// + /// Transform between devices with ICC profiles. + /// + /// + /// + /// using Image @out = in.IccTransform(outputProfile, pcs: Enums.PCS, intent: Enums.Intent, blackPointCompensation: bool, embedded: bool, inputProfile: string, depth: int); + /// + /// + /// Filename to load output profile from. + /// Set Profile Connection Space. + /// Rendering intent. + /// Enable black point compensation. + /// Use embedded input profile, if available. + /// Filename to load input profile from. + /// Output device space depth in bits. + /// A new . + public Image IccTransform(string outputProfile, Enums.PCS? pcs = null, Enums.Intent? intent = null, bool? blackPointCompensation = null, bool? embedded = null, string inputProfile = null, int? depth = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(pcs), pcs); + options.AddIfPresent(nameof(intent), intent); + options.AddIfPresent("black_point_compensation", blackPointCompensation); + options.AddIfPresent(nameof(embedded), embedded); + options.AddIfPresent("input_profile", inputProfile); + options.AddIfPresent(nameof(depth), depth); + + return this.Call("icc_transform", options, outputProfile) as Image; + } + + /// + /// Make a 1D image where pixel values are indexes. + /// + /// + /// + /// using Image @out = NetVips.Image.Identity(bands: int, @ushort: bool, size: int); + /// + /// + /// Number of bands in LUT. + /// Create a 16-bit LUT. + /// Size of 16-bit LUT. + /// A new . + public static Image Identity(int? bands = null, bool? @ushort = null, int? size = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(bands), bands); + options.AddIfPresent("ushort", @ushort); + options.AddIfPresent(nameof(size), size); + + return Operation.Call("identity", options) as Image; + } + + /// + /// Insert image @sub into @main at @x, @y. + /// + /// + /// + /// using Image @out = main.Insert(sub, x, y, expand: bool, background: double[]); + /// + /// + /// Sub-image to insert into main image. + /// Left edge of sub in main. + /// Top edge of sub in main. + /// Expand output to hold all of both inputs. + /// Color for new pixels. + /// A new . + public Image Insert(Image sub, int x, int y, bool? expand = null, double[] background = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(expand), expand); + options.AddIfPresent(nameof(background), background); + + return this.Call("insert", options, sub, x, y) as Image; + } + + /// + /// Invert an image. + /// + /// + /// + /// using Image @out = in.Invert(); + /// + /// + /// A new . + public Image Invert() + { + return this.Call("invert") as Image; + } + + /// + /// Build an inverted look-up table. + /// + /// + /// + /// using Image @out = in.Invertlut(size: int); + /// + /// + /// LUT size to generate. + /// A new . + public Image Invertlut(int? size = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(size), size); + + return this.Call("invertlut", options) as Image; + } + + /// + /// Inverse FFT. + /// + /// + /// + /// using Image @out = in.Invfft(real: bool); + /// + /// + /// Output only the real part of the transform. + /// A new . + public Image Invfft(bool? real = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(real), real); + + return this.Call("invfft", options) as Image; + } + + /// + /// Join a pair of images. + /// + /// + /// + /// using Image @out = in1.Join(in2, direction, expand: bool, shim: int, background: double[], align: Enums.Align); + /// + /// + /// Second input image. + /// Join left-right or up-down. + /// Expand output to hold all of both inputs. + /// Pixels between images. + /// Colour for new pixels. + /// Align on the low, centre or high coordinate edge. + /// A new . + public Image Join(Image in2, Enums.Direction direction, bool? expand = null, int? shim = null, double[] background = null, Enums.Align? align = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(expand), expand); + options.AddIfPresent(nameof(shim), shim); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent(nameof(align), align); + + return this.Call("join", options, in2, direction) as Image; + } + + /// + /// Load JPEG2000 image. + /// + /// + /// + /// using Image @out = NetVips.Image.Jp2kload(filename, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Load this page from the image. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Jp2kload(string filename, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("jp2kload", options, filename) as Image; + } + + /// + /// Load JPEG2000 image. + /// + /// + /// + /// using Image @out = NetVips.Image.Jp2kload(filename, out var flags, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// Load this page from the image. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Jp2kload(string filename, out Enums.ForeignFlags flags, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("jp2kload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load JPEG2000 image. + /// + /// + /// + /// using Image @out = NetVips.Image.Jp2kloadBuffer(buffer, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Load this page from the image. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Jp2kloadBuffer(byte[] buffer, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("jp2kload_buffer", options, buffer) as Image; + } + + /// + /// Load JPEG2000 image. + /// + /// + /// + /// using Image @out = NetVips.Image.Jp2kloadBuffer(buffer, out var flags, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Flags for this file. + /// Load this page from the image. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Jp2kloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("jp2kload_buffer", options, buffer) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load JPEG2000 image. + /// + /// + /// + /// using Image @out = NetVips.Image.Jp2kloadSource(source, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Load this page from the image. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Jp2kloadSource(Source source, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("jp2kload_source", options, source) as Image; + } + + /// + /// Load JPEG2000 image. + /// + /// + /// + /// using Image @out = NetVips.Image.Jp2kloadStream(stream, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Load this page from the image. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Jp2kloadStream(Stream stream, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = Jp2kloadSource(source, page, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load JPEG2000 image. + /// + /// + /// + /// using Image @out = NetVips.Image.Jp2kloadSource(source, out var flags, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Flags for this file. + /// Load this page from the image. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Jp2kloadSource(Source source, out Enums.ForeignFlags flags, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("jp2kload_source", options, source) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load JPEG2000 image. + /// + /// + /// + /// using Image @out = NetVips.Image.Jp2kloadStream(stream, out var flags, page: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Flags for this file. + /// Load this page from the image. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Jp2kloadStream(Stream stream, out Enums.ForeignFlags flags, int? page = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = Jp2kloadSource(source, out flags, page, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Save image in JPEG2000 format. + /// + /// + /// + /// in.Jp2ksave(filename, tileWidth: int, tileHeight: int, lossless: bool, q: int, subsampleMode: Enums.ForeignSubsample, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Filename to load from. + /// Tile width in pixels. + /// Tile height in pixels. + /// Enable lossless compression. + /// Q factor. + /// Select chroma subsample operation mode. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Jp2ksave(string filename, int? tileWidth = null, int? tileHeight = null, bool? lossless = null, int? q = null, Enums.ForeignSubsample? subsampleMode = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent("tile_width", tileWidth); + options.AddIfPresent("tile_height", tileHeight); + options.AddIfPresent(nameof(lossless), lossless); + options.AddIfPresent("Q", q); + options.AddIfPresent("subsample_mode", subsampleMode); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("jp2ksave", options, filename); + } + + /// + /// Save image in JPEG2000 format. + /// + /// + /// + /// byte[] buffer = in.Jp2ksaveBuffer(tileWidth: int, tileHeight: int, lossless: bool, q: int, subsampleMode: Enums.ForeignSubsample, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Tile width in pixels. + /// Tile height in pixels. + /// Enable lossless compression. + /// Q factor. + /// Select chroma subsample operation mode. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + /// An array of bytes. + public byte[] Jp2ksaveBuffer(int? tileWidth = null, int? tileHeight = null, bool? lossless = null, int? q = null, Enums.ForeignSubsample? subsampleMode = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent("tile_width", tileWidth); + options.AddIfPresent("tile_height", tileHeight); + options.AddIfPresent(nameof(lossless), lossless); + options.AddIfPresent("Q", q); + options.AddIfPresent("subsample_mode", subsampleMode); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + return this.Call("jp2ksave_buffer", options) as byte[]; + } + + /// + /// Save image in JPEG2000 format. + /// + /// + /// + /// in.Jp2ksaveTarget(target, tileWidth: int, tileHeight: int, lossless: bool, q: int, subsampleMode: Enums.ForeignSubsample, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Target to save to. + /// Tile width in pixels. + /// Tile height in pixels. + /// Enable lossless compression. + /// Q factor. + /// Select chroma subsample operation mode. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Jp2ksaveTarget(Target target, int? tileWidth = null, int? tileHeight = null, bool? lossless = null, int? q = null, Enums.ForeignSubsample? subsampleMode = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent("tile_width", tileWidth); + options.AddIfPresent("tile_height", tileHeight); + options.AddIfPresent(nameof(lossless), lossless); + options.AddIfPresent("Q", q); + options.AddIfPresent("subsample_mode", subsampleMode); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("jp2ksave_target", options, target); + } + + /// + /// Save image in JPEG2000 format. + /// + /// + /// + /// in.Jp2ksaveStream(stream, tileWidth: int, tileHeight: int, lossless: bool, q: int, subsampleMode: Enums.ForeignSubsample, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Stream to save to. + /// Tile width in pixels. + /// Tile height in pixels. + /// Enable lossless compression. + /// Q factor. + /// Select chroma subsample operation mode. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Jp2ksaveStream(Stream stream, int? tileWidth = null, int? tileHeight = null, bool? lossless = null, int? q = null, Enums.ForeignSubsample? subsampleMode = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + using var target = TargetStream.NewFromStream(stream); + Jp2ksaveTarget(target, tileWidth, tileHeight, lossless, q, subsampleMode, keep, background, pageHeight, profile); + } + + /// + /// Load jpeg from file. + /// + /// + /// + /// using Image @out = NetVips.Image.Jpegload(filename, shrink: int, autorotate: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Shrink factor on load. + /// Rotate image using exif orientation. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Jpegload(string filename, int? shrink = null, bool? autorotate = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(shrink), shrink); + options.AddIfPresent(nameof(autorotate), autorotate); + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("jpegload", options, filename) as Image; + } + + /// + /// Load jpeg from file. + /// + /// + /// + /// using Image @out = NetVips.Image.Jpegload(filename, out var flags, shrink: int, autorotate: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// Shrink factor on load. + /// Rotate image using exif orientation. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Jpegload(string filename, out Enums.ForeignFlags flags, int? shrink = null, bool? autorotate = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(shrink), shrink); + options.AddIfPresent(nameof(autorotate), autorotate); + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("jpegload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load jpeg from buffer. + /// + /// + /// + /// using Image @out = NetVips.Image.JpegloadBuffer(buffer, shrink: int, autorotate: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Shrink factor on load. + /// Rotate image using exif orientation. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image JpegloadBuffer(byte[] buffer, int? shrink = null, bool? autorotate = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(shrink), shrink); + options.AddIfPresent(nameof(autorotate), autorotate); + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("jpegload_buffer", options, buffer) as Image; + } + + /// + /// Load jpeg from buffer. + /// + /// + /// + /// using Image @out = NetVips.Image.JpegloadBuffer(buffer, out var flags, shrink: int, autorotate: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Flags for this file. + /// Shrink factor on load. + /// Rotate image using exif orientation. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image JpegloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, int? shrink = null, bool? autorotate = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(shrink), shrink); + options.AddIfPresent(nameof(autorotate), autorotate); + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("jpegload_buffer", options, buffer) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load image from jpeg source. + /// + /// + /// + /// using Image @out = NetVips.Image.JpegloadSource(source, shrink: int, autorotate: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Shrink factor on load. + /// Rotate image using exif orientation. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image JpegloadSource(Source source, int? shrink = null, bool? autorotate = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(shrink), shrink); + options.AddIfPresent(nameof(autorotate), autorotate); + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("jpegload_source", options, source) as Image; + } + + /// + /// Load image from jpeg stream. + /// + /// + /// + /// using Image @out = NetVips.Image.JpegloadStream(stream, shrink: int, autorotate: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Shrink factor on load. + /// Rotate image using exif orientation. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image JpegloadStream(Stream stream, int? shrink = null, bool? autorotate = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = JpegloadSource(source, shrink, autorotate, unlimited, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load image from jpeg source. + /// + /// + /// + /// using Image @out = NetVips.Image.JpegloadSource(source, out var flags, shrink: int, autorotate: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Flags for this file. + /// Shrink factor on load. + /// Rotate image using exif orientation. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image JpegloadSource(Source source, out Enums.ForeignFlags flags, int? shrink = null, bool? autorotate = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(shrink), shrink); + options.AddIfPresent(nameof(autorotate), autorotate); + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("jpegload_source", options, source) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load image from jpeg stream. + /// + /// + /// + /// using Image @out = NetVips.Image.JpegloadStream(stream, out var flags, shrink: int, autorotate: bool, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Flags for this file. + /// Shrink factor on load. + /// Rotate image using exif orientation. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image JpegloadStream(Stream stream, out Enums.ForeignFlags flags, int? shrink = null, bool? autorotate = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = JpegloadSource(source, out flags, shrink, autorotate, unlimited, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Save image to jpeg file. + /// + /// + /// + /// in.Jpegsave(filename, q: int, optimizeCoding: bool, interlace: bool, trellisQuant: bool, overshootDeringing: bool, optimizeScans: bool, quantTable: int, subsampleMode: Enums.ForeignSubsample, restartInterval: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Filename to save to. + /// Q factor. + /// Compute optimal Huffman coding tables. + /// Generate an interlaced (progressive) jpeg. + /// Apply trellis quantisation to each 8x8 block. + /// Apply overshooting to samples with extreme values. + /// Split spectrum of DCT coefficients into separate scans. + /// Use predefined quantization table with given index. + /// Select chroma subsample operation mode. + /// Add restart markers every specified number of mcu. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Jpegsave(string filename, int? q = null, bool? optimizeCoding = null, bool? interlace = null, bool? trellisQuant = null, bool? overshootDeringing = null, bool? optimizeScans = null, int? quantTable = null, Enums.ForeignSubsample? subsampleMode = null, int? restartInterval = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent("Q", q); + options.AddIfPresent("optimize_coding", optimizeCoding); + options.AddIfPresent(nameof(interlace), interlace); + options.AddIfPresent("trellis_quant", trellisQuant); + options.AddIfPresent("overshoot_deringing", overshootDeringing); + options.AddIfPresent("optimize_scans", optimizeScans); + options.AddIfPresent("quant_table", quantTable); + options.AddIfPresent("subsample_mode", subsampleMode); + options.AddIfPresent("restart_interval", restartInterval); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("jpegsave", options, filename); + } + + /// + /// Save image to jpeg buffer. + /// + /// + /// + /// byte[] buffer = in.JpegsaveBuffer(q: int, optimizeCoding: bool, interlace: bool, trellisQuant: bool, overshootDeringing: bool, optimizeScans: bool, quantTable: int, subsampleMode: Enums.ForeignSubsample, restartInterval: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Q factor. + /// Compute optimal Huffman coding tables. + /// Generate an interlaced (progressive) jpeg. + /// Apply trellis quantisation to each 8x8 block. + /// Apply overshooting to samples with extreme values. + /// Split spectrum of DCT coefficients into separate scans. + /// Use predefined quantization table with given index. + /// Select chroma subsample operation mode. + /// Add restart markers every specified number of mcu. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + /// An array of bytes. + public byte[] JpegsaveBuffer(int? q = null, bool? optimizeCoding = null, bool? interlace = null, bool? trellisQuant = null, bool? overshootDeringing = null, bool? optimizeScans = null, int? quantTable = null, Enums.ForeignSubsample? subsampleMode = null, int? restartInterval = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent("Q", q); + options.AddIfPresent("optimize_coding", optimizeCoding); + options.AddIfPresent(nameof(interlace), interlace); + options.AddIfPresent("trellis_quant", trellisQuant); + options.AddIfPresent("overshoot_deringing", overshootDeringing); + options.AddIfPresent("optimize_scans", optimizeScans); + options.AddIfPresent("quant_table", quantTable); + options.AddIfPresent("subsample_mode", subsampleMode); + options.AddIfPresent("restart_interval", restartInterval); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + return this.Call("jpegsave_buffer", options) as byte[]; + } + + /// + /// Save image to jpeg mime. + /// + /// + /// + /// in.JpegsaveMime(q: int, optimizeCoding: bool, interlace: bool, trellisQuant: bool, overshootDeringing: bool, optimizeScans: bool, quantTable: int, subsampleMode: Enums.ForeignSubsample, restartInterval: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Q factor. + /// Compute optimal Huffman coding tables. + /// Generate an interlaced (progressive) jpeg. + /// Apply trellis quantisation to each 8x8 block. + /// Apply overshooting to samples with extreme values. + /// Split spectrum of DCT coefficients into separate scans. + /// Use predefined quantization table with given index. + /// Select chroma subsample operation mode. + /// Add restart markers every specified number of mcu. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void JpegsaveMime(int? q = null, bool? optimizeCoding = null, bool? interlace = null, bool? trellisQuant = null, bool? overshootDeringing = null, bool? optimizeScans = null, int? quantTable = null, Enums.ForeignSubsample? subsampleMode = null, int? restartInterval = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent("Q", q); + options.AddIfPresent("optimize_coding", optimizeCoding); + options.AddIfPresent(nameof(interlace), interlace); + options.AddIfPresent("trellis_quant", trellisQuant); + options.AddIfPresent("overshoot_deringing", overshootDeringing); + options.AddIfPresent("optimize_scans", optimizeScans); + options.AddIfPresent("quant_table", quantTable); + options.AddIfPresent("subsample_mode", subsampleMode); + options.AddIfPresent("restart_interval", restartInterval); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("jpegsave_mime", options); + } + + /// + /// Save image to jpeg target. + /// + /// + /// + /// in.JpegsaveTarget(target, q: int, optimizeCoding: bool, interlace: bool, trellisQuant: bool, overshootDeringing: bool, optimizeScans: bool, quantTable: int, subsampleMode: Enums.ForeignSubsample, restartInterval: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Target to save to. + /// Q factor. + /// Compute optimal Huffman coding tables. + /// Generate an interlaced (progressive) jpeg. + /// Apply trellis quantisation to each 8x8 block. + /// Apply overshooting to samples with extreme values. + /// Split spectrum of DCT coefficients into separate scans. + /// Use predefined quantization table with given index. + /// Select chroma subsample operation mode. + /// Add restart markers every specified number of mcu. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void JpegsaveTarget(Target target, int? q = null, bool? optimizeCoding = null, bool? interlace = null, bool? trellisQuant = null, bool? overshootDeringing = null, bool? optimizeScans = null, int? quantTable = null, Enums.ForeignSubsample? subsampleMode = null, int? restartInterval = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent("Q", q); + options.AddIfPresent("optimize_coding", optimizeCoding); + options.AddIfPresent(nameof(interlace), interlace); + options.AddIfPresent("trellis_quant", trellisQuant); + options.AddIfPresent("overshoot_deringing", overshootDeringing); + options.AddIfPresent("optimize_scans", optimizeScans); + options.AddIfPresent("quant_table", quantTable); + options.AddIfPresent("subsample_mode", subsampleMode); + options.AddIfPresent("restart_interval", restartInterval); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("jpegsave_target", options, target); + } + + /// + /// Save image to jpeg stream. + /// + /// + /// + /// in.JpegsaveStream(stream, q: int, optimizeCoding: bool, interlace: bool, trellisQuant: bool, overshootDeringing: bool, optimizeScans: bool, quantTable: int, subsampleMode: Enums.ForeignSubsample, restartInterval: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Stream to save to. + /// Q factor. + /// Compute optimal Huffman coding tables. + /// Generate an interlaced (progressive) jpeg. + /// Apply trellis quantisation to each 8x8 block. + /// Apply overshooting to samples with extreme values. + /// Split spectrum of DCT coefficients into separate scans. + /// Use predefined quantization table with given index. + /// Select chroma subsample operation mode. + /// Add restart markers every specified number of mcu. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void JpegsaveStream(Stream stream, int? q = null, bool? optimizeCoding = null, bool? interlace = null, bool? trellisQuant = null, bool? overshootDeringing = null, bool? optimizeScans = null, int? quantTable = null, Enums.ForeignSubsample? subsampleMode = null, int? restartInterval = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + using var target = TargetStream.NewFromStream(stream); + JpegsaveTarget(target, q, optimizeCoding, interlace, trellisQuant, overshootDeringing, optimizeScans, quantTable, subsampleMode, restartInterval, keep, background, pageHeight, profile); + } + + /// + /// Load JPEG-XL image. + /// + /// + /// + /// using Image @out = NetVips.Image.Jxlload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Jxlload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("jxlload", options, filename) as Image; + } + + /// + /// Load JPEG-XL image. + /// + /// + /// + /// using Image @out = NetVips.Image.Jxlload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Jxlload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("jxlload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load JPEG-XL image. + /// + /// + /// + /// using Image @out = NetVips.Image.JxlloadBuffer(buffer, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image JxlloadBuffer(byte[] buffer, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("jxlload_buffer", options, buffer) as Image; + } + + /// + /// Load JPEG-XL image. + /// + /// + /// + /// using Image @out = NetVips.Image.JxlloadBuffer(buffer, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image JxlloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("jxlload_buffer", options, buffer) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load JPEG-XL image. + /// + /// + /// + /// using Image @out = NetVips.Image.JxlloadSource(source, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image JxlloadSource(Source source, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("jxlload_source", options, source) as Image; + } + + /// + /// Load JPEG-XL image. + /// + /// + /// + /// using Image @out = NetVips.Image.JxlloadStream(stream, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image JxlloadStream(Stream stream, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = JxlloadSource(source, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load JPEG-XL image. + /// + /// + /// + /// using Image @out = NetVips.Image.JxlloadSource(source, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image JxlloadSource(Source source, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("jxlload_source", options, source) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load JPEG-XL image. + /// + /// + /// + /// using Image @out = NetVips.Image.JxlloadStream(stream, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image JxlloadStream(Stream stream, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = JxlloadSource(source, out flags, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Save image in JPEG-XL format. + /// + /// + /// + /// in.Jxlsave(filename, tier: int, distance: double, effort: int, lossless: bool, q: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Filename to load from. + /// Decode speed tier. + /// Target butteraugli distance. + /// Encoding effort. + /// Enable lossless compression. + /// Quality factor. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Jxlsave(string filename, int? tier = null, double? distance = null, int? effort = null, bool? lossless = null, int? q = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(tier), tier); + options.AddIfPresent(nameof(distance), distance); + options.AddIfPresent(nameof(effort), effort); + options.AddIfPresent(nameof(lossless), lossless); + options.AddIfPresent("Q", q); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("jxlsave", options, filename); + } + + /// + /// Save image in JPEG-XL format. + /// + /// + /// + /// byte[] buffer = in.JxlsaveBuffer(tier: int, distance: double, effort: int, lossless: bool, q: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Decode speed tier. + /// Target butteraugli distance. + /// Encoding effort. + /// Enable lossless compression. + /// Quality factor. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + /// An array of bytes. + public byte[] JxlsaveBuffer(int? tier = null, double? distance = null, int? effort = null, bool? lossless = null, int? q = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(tier), tier); + options.AddIfPresent(nameof(distance), distance); + options.AddIfPresent(nameof(effort), effort); + options.AddIfPresent(nameof(lossless), lossless); + options.AddIfPresent("Q", q); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + return this.Call("jxlsave_buffer", options) as byte[]; + } + + /// + /// Save image in JPEG-XL format. + /// + /// + /// + /// in.JxlsaveTarget(target, tier: int, distance: double, effort: int, lossless: bool, q: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Target to save to. + /// Decode speed tier. + /// Target butteraugli distance. + /// Encoding effort. + /// Enable lossless compression. + /// Quality factor. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void JxlsaveTarget(Target target, int? tier = null, double? distance = null, int? effort = null, bool? lossless = null, int? q = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(tier), tier); + options.AddIfPresent(nameof(distance), distance); + options.AddIfPresent(nameof(effort), effort); + options.AddIfPresent(nameof(lossless), lossless); + options.AddIfPresent("Q", q); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("jxlsave_target", options, target); + } + + /// + /// Save image in JPEG-XL format. + /// + /// + /// + /// in.JxlsaveStream(stream, tier: int, distance: double, effort: int, lossless: bool, q: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Stream to save to. + /// Decode speed tier. + /// Target butteraugli distance. + /// Encoding effort. + /// Enable lossless compression. + /// Quality factor. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void JxlsaveStream(Stream stream, int? tier = null, double? distance = null, int? effort = null, bool? lossless = null, int? q = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + using var target = TargetStream.NewFromStream(stream); + JxlsaveTarget(target, tier, distance, effort, lossless, q, keep, background, pageHeight, profile); + } + + /// + /// Transform float Lab to LabQ coding. + /// + /// + /// + /// using Image @out = in.Lab2LabQ(); + /// + /// + /// A new . + public Image Lab2LabQ() + { + return this.Call("Lab2LabQ") as Image; + } + + /// + /// Transform float Lab to signed short. + /// + /// + /// + /// using Image @out = in.Lab2LabS(); + /// + /// + /// A new . + public Image Lab2LabS() + { + return this.Call("Lab2LabS") as Image; + } + + /// + /// Transform Lab to LCh. + /// + /// + /// + /// using Image @out = in.Lab2LCh(); + /// + /// + /// A new . + public Image Lab2LCh() + { + return this.Call("Lab2LCh") as Image; + } + + /// + /// Transform CIELAB to XYZ. + /// + /// + /// + /// using Image @out = in.Lab2XYZ(temp: double[]); + /// + /// + /// Color temperature. + /// A new . + public Image Lab2XYZ(double[] temp = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(temp), temp); + + return this.Call("Lab2XYZ", options) as Image; + } + + /// + /// Label regions in an image. + /// + /// + /// + /// using Image mask = in.Labelregions(); + /// + /// + /// A new . + public Image Labelregions() + { + return this.Call("labelregions") as Image; + } + + /// + /// Label regions in an image. + /// + /// + /// + /// using Image mask = in.Labelregions(out var segments); + /// + /// + /// Number of discrete contiguous regions. + /// A new . + public Image Labelregions(out int segments) + { + var optionalOutput = new VOption { - return this.Call("zoom", xfac, yfac) as Image; - } - - #endregion - - #region auto-generated properties - - /// - /// Image width in pixels - /// - public int Width => (int)Get("width"); - - /// - /// Image height in pixels - /// - public int Height => (int)Get("height"); - - /// - /// Number of bands in image - /// - public int Bands => (int)Get("bands"); - - /// - /// Pixel format in image - /// - public Enums.BandFormat Format => (Enums.BandFormat)Get("format"); - - /// - /// Pixel coding - /// - public Enums.Coding Coding => (Enums.Coding)Get("coding"); - - /// - /// Pixel interpretation - /// - public Enums.Interpretation Interpretation => (Enums.Interpretation)Get("interpretation"); - - /// - /// Horizontal offset of origin - /// - public int Xoffset => (int)Get("xoffset"); - - /// - /// Vertical offset of origin - /// - public int Yoffset => (int)Get("yoffset"); - - /// - /// Horizontal resolution in pixels/mm - /// - public double Xres => (double)Get("xres"); - - /// - /// Vertical resolution in pixels/mm - /// - public double Yres => (double)Get("yres"); - - /// - /// Image filename - /// - public string Filename => (string)Get("filename"); - - #endregion - } -} + {"segments", true} + }; + + var results = this.Call("labelregions", optionalOutput) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + segments = opts?["segments"] is int out1 ? out1 : 0; + + return finalResult; + } + + /// + /// Unpack a LabQ image to float Lab. + /// + /// + /// + /// using Image @out = in.LabQ2Lab(); + /// + /// + /// A new . + public Image LabQ2Lab() + { + return this.Call("LabQ2Lab") as Image; + } + + /// + /// Unpack a LabQ image to short Lab. + /// + /// + /// + /// using Image @out = in.LabQ2LabS(); + /// + /// + /// A new . + public Image LabQ2LabS() + { + return this.Call("LabQ2LabS") as Image; + } + + /// + /// Convert a LabQ image to sRGB. + /// + /// + /// + /// using Image @out = in.LabQ2sRGB(); + /// + /// + /// A new . + public Image LabQ2sRGB() + { + return this.Call("LabQ2sRGB") as Image; + } + + /// + /// Transform signed short Lab to float. + /// + /// + /// + /// using Image @out = in.LabS2Lab(); + /// + /// + /// A new . + public Image LabS2Lab() + { + return this.Call("LabS2Lab") as Image; + } + + /// + /// Transform short Lab to LabQ coding. + /// + /// + /// + /// using Image @out = in.LabS2LabQ(); + /// + /// + /// A new . + public Image LabS2LabQ() + { + return this.Call("LabS2LabQ") as Image; + } + + /// + /// Transform LCh to CMC. + /// + /// + /// + /// using Image @out = in.LCh2CMC(); + /// + /// + /// A new . + public Image LCh2CMC() + { + return this.Call("LCh2CMC") as Image; + } + + /// + /// Transform LCh to Lab. + /// + /// + /// + /// using Image @out = in.LCh2Lab(); + /// + /// + /// A new . + public Image LCh2Lab() + { + return this.Call("LCh2Lab") as Image; + } + + /// + /// Calculate (a * in + b). + /// + /// + /// + /// using Image @out = in.Linear(a, b, uchar: bool); + /// + /// + /// Multiply by this. + /// Add this. + /// Output should be uchar. + /// A new . + public Image Linear(double[] a, double[] b, bool? uchar = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(uchar), uchar); + + return this.Call("linear", options, a, b) as Image; + } + + /// + /// Cache an image as a set of lines. + /// + /// + /// + /// using Image @out = in.Linecache(tileHeight: int, access: Enums.Access, threaded: bool, persistent: bool); + /// + /// + /// Tile height in pixels. + /// Expected access pattern. + /// Allow threaded access. + /// Keep cache between evaluations. + /// A new . + public Image Linecache(int? tileHeight = null, Enums.Access? access = null, bool? threaded = null, bool? persistent = null) + { + var options = new VOption(); + + options.AddIfPresent("tile_height", tileHeight); + options.AddIfPresent(nameof(access), access); + options.AddIfPresent(nameof(threaded), threaded); + options.AddIfPresent(nameof(persistent), persistent); + + return this.Call("linecache", options) as Image; + } + + /// + /// Make a Laplacian of Gaussian image. + /// + /// + /// + /// using Image @out = NetVips.Image.Logmat(sigma, minAmpl, separable: bool, precision: Enums.Precision); + /// + /// + /// Radius of Gaussian. + /// Minimum amplitude of Gaussian. + /// Generate separable Gaussian. + /// Generate with this precision. + /// A new . + public static Image Logmat(double sigma, double minAmpl, bool? separable = null, Enums.Precision? precision = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(separable), separable); + options.AddIfPresent(nameof(precision), precision); + + return Operation.Call("logmat", options, sigma, minAmpl) as Image; + } + + /// + /// Load file with ImageMagick. + /// + /// + /// + /// using Image @out = NetVips.Image.Magickload(filename, density: string, page: int, n: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Canvas resolution for rendering vector formats like SVG. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Magickload(string filename, string density = null, int? page = null, int? n = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(density), density); + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("magickload", options, filename) as Image; + } + + /// + /// Load file with ImageMagick. + /// + /// + /// + /// using Image @out = NetVips.Image.Magickload(filename, out var flags, density: string, page: int, n: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// Canvas resolution for rendering vector formats like SVG. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Magickload(string filename, out Enums.ForeignFlags flags, string density = null, int? page = null, int? n = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(density), density); + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("magickload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load buffer with ImageMagick. + /// + /// + /// + /// using Image @out = NetVips.Image.MagickloadBuffer(buffer, density: string, page: int, n: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Canvas resolution for rendering vector formats like SVG. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image MagickloadBuffer(byte[] buffer, string density = null, int? page = null, int? n = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(density), density); + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("magickload_buffer", options, buffer) as Image; + } + + /// + /// Load buffer with ImageMagick. + /// + /// + /// + /// using Image @out = NetVips.Image.MagickloadBuffer(buffer, out var flags, density: string, page: int, n: int, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Flags for this file. + /// Canvas resolution for rendering vector formats like SVG. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image MagickloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, string density = null, int? page = null, int? n = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(density), density); + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("magickload_buffer", options, buffer) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Save file with ImageMagick. + /// + /// + /// + /// in.Magicksave(filename, format: string, quality: int, optimizeGifFrames: bool, optimizeGifTransparency: bool, bitdepth: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Filename to save to. + /// Format to save in. + /// Quality to use. + /// Apply GIF frames optimization. + /// Apply GIF transparency optimization. + /// Number of bits per pixel. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Magicksave(string filename, string format = null, int? quality = null, bool? optimizeGifFrames = null, bool? optimizeGifTransparency = null, int? bitdepth = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(format), format); + options.AddIfPresent(nameof(quality), quality); + options.AddIfPresent("optimize_gif_frames", optimizeGifFrames); + options.AddIfPresent("optimize_gif_transparency", optimizeGifTransparency); + options.AddIfPresent(nameof(bitdepth), bitdepth); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("magicksave", options, filename); + } + + /// + /// Save image to magick buffer. + /// + /// + /// + /// byte[] buffer = in.MagicksaveBuffer(format: string, quality: int, optimizeGifFrames: bool, optimizeGifTransparency: bool, bitdepth: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Format to save in. + /// Quality to use. + /// Apply GIF frames optimization. + /// Apply GIF transparency optimization. + /// Number of bits per pixel. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + /// An array of bytes. + public byte[] MagicksaveBuffer(string format = null, int? quality = null, bool? optimizeGifFrames = null, bool? optimizeGifTransparency = null, int? bitdepth = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(format), format); + options.AddIfPresent(nameof(quality), quality); + options.AddIfPresent("optimize_gif_frames", optimizeGifFrames); + options.AddIfPresent("optimize_gif_transparency", optimizeGifTransparency); + options.AddIfPresent(nameof(bitdepth), bitdepth); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + return this.Call("magicksave_buffer", options) as byte[]; + } + + /// + /// Resample with a map image. + /// + /// + /// + /// using Image @out = in.Mapim(index, interpolate: GObject, background: double[], premultiplied: bool, extend: Enums.Extend); + /// + /// + /// Index pixels with this. + /// Interpolate pixels with this. + /// Background value. + /// Images have premultiplied alpha. + /// How to generate the extra pixels. + /// A new . + public Image Mapim(Image index, GObject interpolate = null, double[] background = null, bool? premultiplied = null, Enums.Extend? extend = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(interpolate), interpolate); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent(nameof(premultiplied), premultiplied); + options.AddIfPresent(nameof(extend), extend); + + return this.Call("mapim", options, index) as Image; + } + + /// + /// Map an image though a lut. + /// + /// + /// + /// using Image @out = in.Maplut(lut, band: int); + /// + /// + /// Look-up table image. + /// Apply one-band lut to this band of in. + /// A new . + public Image Maplut(Image lut, int? band = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(band), band); + + return this.Call("maplut", options, lut) as Image; + } + + /// + /// Make a butterworth filter. + /// + /// + /// + /// using Image @out = NetVips.Image.MaskButterworth(width, height, order, frequencyCutoff, amplitudeCutoff, uchar: bool, nodc: bool, reject: bool, optical: bool); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Filter order. + /// Frequency cutoff. + /// Amplitude cutoff. + /// Output an unsigned char image. + /// Remove DC component. + /// Invert the sense of the filter. + /// Rotate quadrants to optical space. + /// A new . + public static Image MaskButterworth(int width, int height, double order, double frequencyCutoff, double amplitudeCutoff, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(uchar), uchar); + options.AddIfPresent(nameof(nodc), nodc); + options.AddIfPresent(nameof(reject), reject); + options.AddIfPresent(nameof(optical), optical); + + return Operation.Call("mask_butterworth", options, width, height, order, frequencyCutoff, amplitudeCutoff) as Image; + } + + /// + /// Make a butterworth_band filter. + /// + /// + /// + /// using Image @out = NetVips.Image.MaskButterworthBand(width, height, order, frequencyCutoffX, frequencyCutoffY, radius, amplitudeCutoff, uchar: bool, nodc: bool, reject: bool, optical: bool); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Filter order. + /// Frequency cutoff x. + /// Frequency cutoff y. + /// Radius of circle. + /// Amplitude cutoff. + /// Output an unsigned char image. + /// Remove DC component. + /// Invert the sense of the filter. + /// Rotate quadrants to optical space. + /// A new . + public static Image MaskButterworthBand(int width, int height, double order, double frequencyCutoffX, double frequencyCutoffY, double radius, double amplitudeCutoff, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(uchar), uchar); + options.AddIfPresent(nameof(nodc), nodc); + options.AddIfPresent(nameof(reject), reject); + options.AddIfPresent(nameof(optical), optical); + + return Operation.Call("mask_butterworth_band", options, width, height, order, frequencyCutoffX, frequencyCutoffY, radius, amplitudeCutoff) as Image; + } + + /// + /// Make a butterworth ring filter. + /// + /// + /// + /// using Image @out = NetVips.Image.MaskButterworthRing(width, height, order, frequencyCutoff, amplitudeCutoff, ringwidth, uchar: bool, nodc: bool, reject: bool, optical: bool); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Filter order. + /// Frequency cutoff. + /// Amplitude cutoff. + /// Ringwidth. + /// Output an unsigned char image. + /// Remove DC component. + /// Invert the sense of the filter. + /// Rotate quadrants to optical space. + /// A new . + public static Image MaskButterworthRing(int width, int height, double order, double frequencyCutoff, double amplitudeCutoff, double ringwidth, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(uchar), uchar); + options.AddIfPresent(nameof(nodc), nodc); + options.AddIfPresent(nameof(reject), reject); + options.AddIfPresent(nameof(optical), optical); + + return Operation.Call("mask_butterworth_ring", options, width, height, order, frequencyCutoff, amplitudeCutoff, ringwidth) as Image; + } + + /// + /// Make fractal filter. + /// + /// + /// + /// using Image @out = NetVips.Image.MaskFractal(width, height, fractalDimension, uchar: bool, nodc: bool, reject: bool, optical: bool); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Fractal dimension. + /// Output an unsigned char image. + /// Remove DC component. + /// Invert the sense of the filter. + /// Rotate quadrants to optical space. + /// A new . + public static Image MaskFractal(int width, int height, double fractalDimension, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(uchar), uchar); + options.AddIfPresent(nameof(nodc), nodc); + options.AddIfPresent(nameof(reject), reject); + options.AddIfPresent(nameof(optical), optical); + + return Operation.Call("mask_fractal", options, width, height, fractalDimension) as Image; + } + + /// + /// Make a gaussian filter. + /// + /// + /// + /// using Image @out = NetVips.Image.MaskGaussian(width, height, frequencyCutoff, amplitudeCutoff, uchar: bool, nodc: bool, reject: bool, optical: bool); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Frequency cutoff. + /// Amplitude cutoff. + /// Output an unsigned char image. + /// Remove DC component. + /// Invert the sense of the filter. + /// Rotate quadrants to optical space. + /// A new . + public static Image MaskGaussian(int width, int height, double frequencyCutoff, double amplitudeCutoff, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(uchar), uchar); + options.AddIfPresent(nameof(nodc), nodc); + options.AddIfPresent(nameof(reject), reject); + options.AddIfPresent(nameof(optical), optical); + + return Operation.Call("mask_gaussian", options, width, height, frequencyCutoff, amplitudeCutoff) as Image; + } + + /// + /// Make a gaussian filter. + /// + /// + /// + /// using Image @out = NetVips.Image.MaskGaussianBand(width, height, frequencyCutoffX, frequencyCutoffY, radius, amplitudeCutoff, uchar: bool, nodc: bool, reject: bool, optical: bool); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Frequency cutoff x. + /// Frequency cutoff y. + /// Radius of circle. + /// Amplitude cutoff. + /// Output an unsigned char image. + /// Remove DC component. + /// Invert the sense of the filter. + /// Rotate quadrants to optical space. + /// A new . + public static Image MaskGaussianBand(int width, int height, double frequencyCutoffX, double frequencyCutoffY, double radius, double amplitudeCutoff, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(uchar), uchar); + options.AddIfPresent(nameof(nodc), nodc); + options.AddIfPresent(nameof(reject), reject); + options.AddIfPresent(nameof(optical), optical); + + return Operation.Call("mask_gaussian_band", options, width, height, frequencyCutoffX, frequencyCutoffY, radius, amplitudeCutoff) as Image; + } + + /// + /// Make a gaussian ring filter. + /// + /// + /// + /// using Image @out = NetVips.Image.MaskGaussianRing(width, height, frequencyCutoff, amplitudeCutoff, ringwidth, uchar: bool, nodc: bool, reject: bool, optical: bool); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Frequency cutoff. + /// Amplitude cutoff. + /// Ringwidth. + /// Output an unsigned char image. + /// Remove DC component. + /// Invert the sense of the filter. + /// Rotate quadrants to optical space. + /// A new . + public static Image MaskGaussianRing(int width, int height, double frequencyCutoff, double amplitudeCutoff, double ringwidth, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(uchar), uchar); + options.AddIfPresent(nameof(nodc), nodc); + options.AddIfPresent(nameof(reject), reject); + options.AddIfPresent(nameof(optical), optical); + + return Operation.Call("mask_gaussian_ring", options, width, height, frequencyCutoff, amplitudeCutoff, ringwidth) as Image; + } + + /// + /// Make an ideal filter. + /// + /// + /// + /// using Image @out = NetVips.Image.MaskIdeal(width, height, frequencyCutoff, uchar: bool, nodc: bool, reject: bool, optical: bool); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Frequency cutoff. + /// Output an unsigned char image. + /// Remove DC component. + /// Invert the sense of the filter. + /// Rotate quadrants to optical space. + /// A new . + public static Image MaskIdeal(int width, int height, double frequencyCutoff, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(uchar), uchar); + options.AddIfPresent(nameof(nodc), nodc); + options.AddIfPresent(nameof(reject), reject); + options.AddIfPresent(nameof(optical), optical); + + return Operation.Call("mask_ideal", options, width, height, frequencyCutoff) as Image; + } + + /// + /// Make an ideal band filter. + /// + /// + /// + /// using Image @out = NetVips.Image.MaskIdealBand(width, height, frequencyCutoffX, frequencyCutoffY, radius, uchar: bool, nodc: bool, reject: bool, optical: bool); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Frequency cutoff x. + /// Frequency cutoff y. + /// Radius of circle. + /// Output an unsigned char image. + /// Remove DC component. + /// Invert the sense of the filter. + /// Rotate quadrants to optical space. + /// A new . + public static Image MaskIdealBand(int width, int height, double frequencyCutoffX, double frequencyCutoffY, double radius, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(uchar), uchar); + options.AddIfPresent(nameof(nodc), nodc); + options.AddIfPresent(nameof(reject), reject); + options.AddIfPresent(nameof(optical), optical); + + return Operation.Call("mask_ideal_band", options, width, height, frequencyCutoffX, frequencyCutoffY, radius) as Image; + } + + /// + /// Make an ideal ring filter. + /// + /// + /// + /// using Image @out = NetVips.Image.MaskIdealRing(width, height, frequencyCutoff, ringwidth, uchar: bool, nodc: bool, reject: bool, optical: bool); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Frequency cutoff. + /// Ringwidth. + /// Output an unsigned char image. + /// Remove DC component. + /// Invert the sense of the filter. + /// Rotate quadrants to optical space. + /// A new . + public static Image MaskIdealRing(int width, int height, double frequencyCutoff, double ringwidth, bool? uchar = null, bool? nodc = null, bool? reject = null, bool? optical = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(uchar), uchar); + options.AddIfPresent(nameof(nodc), nodc); + options.AddIfPresent(nameof(reject), reject); + options.AddIfPresent(nameof(optical), optical); + + return Operation.Call("mask_ideal_ring", options, width, height, frequencyCutoff, ringwidth) as Image; + } + + /// + /// First-order match of two images. + /// + /// + /// + /// using Image @out = ref.Match(sec, xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, hwindow: int, harea: int, search: bool, interpolate: GObject); + /// + /// + /// Secondary image. + /// Position of first reference tie-point. + /// Position of first reference tie-point. + /// Position of first secondary tie-point. + /// Position of first secondary tie-point. + /// Position of second reference tie-point. + /// Position of second reference tie-point. + /// Position of second secondary tie-point. + /// Position of second secondary tie-point. + /// Half window size. + /// Half area size. + /// Search to improve tie-points. + /// Interpolate pixels with this. + /// A new . + public Image Match(Image sec, int xr1, int yr1, int xs1, int ys1, int xr2, int yr2, int xs2, int ys2, int? hwindow = null, int? harea = null, bool? search = null, GObject interpolate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(hwindow), hwindow); + options.AddIfPresent(nameof(harea), harea); + options.AddIfPresent(nameof(search), search); + options.AddIfPresent(nameof(interpolate), interpolate); + + return this.Call("match", options, sec, xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2) as Image; + } + + /// + /// Apply a math operation to an image. + /// + /// + /// + /// using Image @out = in.Math(math); + /// + /// + /// Math to perform. + /// A new . + public Image Math(Enums.OperationMath math) + { + return this.Call("math", math) as Image; + } + + /// + /// Binary math operations. + /// + /// + /// + /// using Image @out = left.Math2(right, math2); + /// + /// + /// Right-hand image argument. + /// Math to perform. + /// A new . + public Image Math2(Image right, Enums.OperationMath2 math2) + { + return this.Call("math2", right, math2) as Image; + } + + /// + /// Binary math operations with a constant. + /// + /// + /// + /// using Image @out = in.Math2Const(math2, c); + /// + /// + /// Math to perform. + /// Array of constants. + /// A new . + public Image Math2Const(Enums.OperationMath2 math2, double[] c) + { + return this.Call("math2_const", math2, c) as Image; + } + + /// + /// Load mat from file. + /// + /// + /// + /// using Image @out = NetVips.Image.Matload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Matload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("matload", options, filename) as Image; + } + + /// + /// Load mat from file. + /// + /// + /// + /// using Image @out = NetVips.Image.Matload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Matload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("matload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Invert an matrix. + /// + /// + /// + /// using Image @out = in.Matrixinvert(); + /// + /// + /// A new . + public Image Matrixinvert() + { + return this.Call("matrixinvert") as Image; + } + + /// + /// Load matrix. + /// + /// + /// + /// using Image @out = NetVips.Image.Matrixload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Matrixload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("matrixload", options, filename) as Image; + } + + /// + /// Load matrix. + /// + /// + /// + /// using Image @out = NetVips.Image.Matrixload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Matrixload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("matrixload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load matrix. + /// + /// + /// + /// using Image @out = NetVips.Image.MatrixloadSource(source, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image MatrixloadSource(Source source, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("matrixload_source", options, source) as Image; + } + + /// + /// Load matrix. + /// + /// + /// + /// using Image @out = NetVips.Image.MatrixloadStream(stream, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image MatrixloadStream(Stream stream, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = MatrixloadSource(source, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load matrix. + /// + /// + /// + /// using Image @out = NetVips.Image.MatrixloadSource(source, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image MatrixloadSource(Source source, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("matrixload_source", options, source) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load matrix. + /// + /// + /// + /// using Image @out = NetVips.Image.MatrixloadStream(stream, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image MatrixloadStream(Stream stream, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = MatrixloadSource(source, out flags, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Print matrix. + /// + /// + /// + /// in.Matrixprint(keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Matrixprint(Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("matrixprint", options); + } + + /// + /// Save image to matrix. + /// + /// + /// + /// in.Matrixsave(filename, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Filename to save to. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Matrixsave(string filename, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("matrixsave", options, filename); + } + + /// + /// Save image to matrix. + /// + /// + /// + /// in.MatrixsaveTarget(target, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Target to save to. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void MatrixsaveTarget(Target target, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("matrixsave_target", options, target); + } + + /// + /// Save image to matrix. + /// + /// + /// + /// in.MatrixsaveStream(stream, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Stream to save to. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void MatrixsaveStream(Stream stream, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + using var target = TargetStream.NewFromStream(stream); + MatrixsaveTarget(target, keep, background, pageHeight, profile); + } + + /// + /// Find image maximum. + /// + /// + /// + /// double @out = in.Max(size: int); + /// + /// + /// Number of maximum values to find. + /// A double. + public double Max(int? size = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(size), size); + + return this.Call("max", options) is double result ? result : 0d; + } + + /// + /// Find image maximum. + /// + /// + /// + /// double @out = in.Max(out var x, size: int); + /// + /// + /// Horizontal position of maximum. + /// Number of maximum values to find. + /// A double. + public double Max(out int x, int? size = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(size), size); + + options.Add("x", true); + + var results = this.Call("max", options) as object[]; + var finalResult = results?[0] is double result ? result : 0d; + var opts = results?[1] as VOption; + x = opts?["x"] is int out1 ? out1 : 0; + + return finalResult; + } + + /// + /// Find image maximum. + /// + /// + /// + /// double @out = in.Max(out var x, out var y, size: int); + /// + /// + /// Horizontal position of maximum. + /// Vertical position of maximum. + /// Number of maximum values to find. + /// A double. + public double Max(out int x, out int y, int? size = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(size), size); + + options.Add("x", true); + options.Add("y", true); + + var results = this.Call("max", options) as object[]; + var finalResult = results?[0] is double result ? result : 0d; + var opts = results?[1] as VOption; + x = opts?["x"] is int out1 ? out1 : 0; + y = opts?["y"] is int out2 ? out2 : 0; + + return finalResult; + } + + /// + /// Find image maximum. + /// + /// + /// + /// double @out = in.Max(out var x, out var y, out var outArray, size: int); + /// + /// + /// Horizontal position of maximum. + /// Vertical position of maximum. + /// Array of output values. + /// Number of maximum values to find. + /// A double. + public double Max(out int x, out int y, out double[] outArray, int? size = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(size), size); + + options.Add("x", true); + options.Add("y", true); + options.Add("out_array", true); + + var results = this.Call("max", options) as object[]; + var finalResult = results?[0] is double result ? result : 0d; + var opts = results?[1] as VOption; + x = opts?["x"] is int out1 ? out1 : 0; + y = opts?["y"] is int out2 ? out2 : 0; + outArray = opts?["out_array"] as double[]; + + return finalResult; + } + + /// + /// Find image maximum. + /// + /// + /// + /// double @out = in.Max(out var x, out var y, out var outArray, out var xArray, size: int); + /// + /// + /// Horizontal position of maximum. + /// Vertical position of maximum. + /// Array of output values. + /// Array of horizontal positions. + /// Number of maximum values to find. + /// A double. + public double Max(out int x, out int y, out double[] outArray, out int[] xArray, int? size = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(size), size); + + options.Add("x", true); + options.Add("y", true); + options.Add("out_array", true); + options.Add("x_array", true); + + var results = this.Call("max", options) as object[]; + var finalResult = results?[0] is double result ? result : 0d; + var opts = results?[1] as VOption; + x = opts?["x"] is int out1 ? out1 : 0; + y = opts?["y"] is int out2 ? out2 : 0; + outArray = opts?["out_array"] as double[]; + xArray = opts?["x_array"] as int[]; + + return finalResult; + } + + /// + /// Find image maximum. + /// + /// + /// + /// double @out = in.Max(out var x, out var y, out var outArray, out var xArray, out var yArray, size: int); + /// + /// + /// Horizontal position of maximum. + /// Vertical position of maximum. + /// Array of output values. + /// Array of horizontal positions. + /// Array of vertical positions. + /// Number of maximum values to find. + /// A double. + public double Max(out int x, out int y, out double[] outArray, out int[] xArray, out int[] yArray, int? size = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(size), size); + + options.Add("x", true); + options.Add("y", true); + options.Add("out_array", true); + options.Add("x_array", true); + options.Add("y_array", true); + + var results = this.Call("max", options) as object[]; + var finalResult = results?[0] is double result ? result : 0d; + var opts = results?[1] as VOption; + x = opts?["x"] is int out1 ? out1 : 0; + y = opts?["y"] is int out2 ? out2 : 0; + outArray = opts?["out_array"] as double[]; + xArray = opts?["x_array"] as int[]; + yArray = opts?["y_array"] as int[]; + + return finalResult; + } + + /// + /// Measure a set of patches on a color chart. + /// + /// + /// + /// using Image @out = in.Measure(h, v, left: int, top: int, width: int, height: int); + /// + /// + /// Number of patches across chart. + /// Number of patches down chart. + /// Left edge of extract area. + /// Top edge of extract area. + /// Width of extract area. + /// Height of extract area. + /// A new . + public Image Measure(int h, int v, int? left = null, int? top = null, int? width = null, int? height = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(left), left); + options.AddIfPresent(nameof(top), top); + options.AddIfPresent(nameof(width), width); + options.AddIfPresent(nameof(height), height); + + return this.Call("measure", options, h, v) as Image; + } + + /// + /// Merge two images. + /// + /// + /// + /// using Image @out = ref.Merge(sec, direction, dx, dy, mblend: int); + /// + /// + /// Secondary image. + /// Horizontal or vertical merge. + /// Horizontal displacement from sec to ref. + /// Vertical displacement from sec to ref. + /// Maximum blend size. + /// A new . + public Image Merge(Image sec, Enums.Direction direction, int dx, int dy, int? mblend = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(mblend), mblend); + + return this.Call("merge", options, sec, direction, dx, dy) as Image; + } + + /// + /// Find image minimum. + /// + /// + /// + /// double @out = in.Min(size: int); + /// + /// + /// Number of minimum values to find. + /// A double. + public double Min(int? size = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(size), size); + + return this.Call("min", options) is double result ? result : 0d; + } + + /// + /// Find image minimum. + /// + /// + /// + /// double @out = in.Min(out var x, size: int); + /// + /// + /// Horizontal position of minimum. + /// Number of minimum values to find. + /// A double. + public double Min(out int x, int? size = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(size), size); + + options.Add("x", true); + + var results = this.Call("min", options) as object[]; + var finalResult = results?[0] is double result ? result : 0d; + var opts = results?[1] as VOption; + x = opts?["x"] is int out1 ? out1 : 0; + + return finalResult; + } + + /// + /// Find image minimum. + /// + /// + /// + /// double @out = in.Min(out var x, out var y, size: int); + /// + /// + /// Horizontal position of minimum. + /// Vertical position of minimum. + /// Number of minimum values to find. + /// A double. + public double Min(out int x, out int y, int? size = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(size), size); + + options.Add("x", true); + options.Add("y", true); + + var results = this.Call("min", options) as object[]; + var finalResult = results?[0] is double result ? result : 0d; + var opts = results?[1] as VOption; + x = opts?["x"] is int out1 ? out1 : 0; + y = opts?["y"] is int out2 ? out2 : 0; + + return finalResult; + } + + /// + /// Find image minimum. + /// + /// + /// + /// double @out = in.Min(out var x, out var y, out var outArray, size: int); + /// + /// + /// Horizontal position of minimum. + /// Vertical position of minimum. + /// Array of output values. + /// Number of minimum values to find. + /// A double. + public double Min(out int x, out int y, out double[] outArray, int? size = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(size), size); + + options.Add("x", true); + options.Add("y", true); + options.Add("out_array", true); + + var results = this.Call("min", options) as object[]; + var finalResult = results?[0] is double result ? result : 0d; + var opts = results?[1] as VOption; + x = opts?["x"] is int out1 ? out1 : 0; + y = opts?["y"] is int out2 ? out2 : 0; + outArray = opts?["out_array"] as double[]; + + return finalResult; + } + + /// + /// Find image minimum. + /// + /// + /// + /// double @out = in.Min(out var x, out var y, out var outArray, out var xArray, size: int); + /// + /// + /// Horizontal position of minimum. + /// Vertical position of minimum. + /// Array of output values. + /// Array of horizontal positions. + /// Number of minimum values to find. + /// A double. + public double Min(out int x, out int y, out double[] outArray, out int[] xArray, int? size = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(size), size); + + options.Add("x", true); + options.Add("y", true); + options.Add("out_array", true); + options.Add("x_array", true); + + var results = this.Call("min", options) as object[]; + var finalResult = results?[0] is double result ? result : 0d; + var opts = results?[1] as VOption; + x = opts?["x"] is int out1 ? out1 : 0; + y = opts?["y"] is int out2 ? out2 : 0; + outArray = opts?["out_array"] as double[]; + xArray = opts?["x_array"] as int[]; + + return finalResult; + } + + /// + /// Find image minimum. + /// + /// + /// + /// double @out = in.Min(out var x, out var y, out var outArray, out var xArray, out var yArray, size: int); + /// + /// + /// Horizontal position of minimum. + /// Vertical position of minimum. + /// Array of output values. + /// Array of horizontal positions. + /// Array of vertical positions. + /// Number of minimum values to find. + /// A double. + public double Min(out int x, out int y, out double[] outArray, out int[] xArray, out int[] yArray, int? size = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(size), size); + + options.Add("x", true); + options.Add("y", true); + options.Add("out_array", true); + options.Add("x_array", true); + options.Add("y_array", true); + + var results = this.Call("min", options) as object[]; + var finalResult = results?[0] is double result ? result : 0d; + var opts = results?[1] as VOption; + x = opts?["x"] is int out1 ? out1 : 0; + y = opts?["y"] is int out2 ? out2 : 0; + outArray = opts?["out_array"] as double[]; + xArray = opts?["x_array"] as int[]; + yArray = opts?["y_array"] as int[]; + + return finalResult; + } + + /// + /// Morphology operation. + /// + /// + /// + /// using Image @out = in.Morph(mask, morph); + /// + /// + /// Input matrix image. + /// Morphological operation to perform. + /// A new . + public Image Morph(Image mask, Enums.OperationMorphology morph) + { + return this.Call("morph", mask, morph) as Image; + } + + /// + /// Mosaic two images. + /// + /// + /// + /// using Image @out = ref.Mosaic(sec, direction, xref, yref, xsec, ysec, hwindow: int, harea: int, mblend: int, bandno: int); + /// + /// + /// Secondary image. + /// Horizontal or vertical mosaic. + /// Position of reference tie-point. + /// Position of reference tie-point. + /// Position of secondary tie-point. + /// Position of secondary tie-point. + /// Half window size. + /// Half area size. + /// Maximum blend size. + /// Band to search for features on. + /// A new . + public Image Mosaic(Image sec, Enums.Direction direction, int xref, int yref, int xsec, int ysec, int? hwindow = null, int? harea = null, int? mblend = null, int? bandno = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(hwindow), hwindow); + options.AddIfPresent(nameof(harea), harea); + options.AddIfPresent(nameof(mblend), mblend); + options.AddIfPresent(nameof(bandno), bandno); + + return this.Call("mosaic", options, sec, direction, xref, yref, xsec, ysec) as Image; + } + + /// + /// Mosaic two images. + /// + /// + /// + /// using Image @out = ref.Mosaic(sec, direction, xref, yref, xsec, ysec, out var dx0, hwindow: int, harea: int, mblend: int, bandno: int); + /// + /// + /// Secondary image. + /// Horizontal or vertical mosaic. + /// Position of reference tie-point. + /// Position of reference tie-point. + /// Position of secondary tie-point. + /// Position of secondary tie-point. + /// Detected integer offset. + /// Half window size. + /// Half area size. + /// Maximum blend size. + /// Band to search for features on. + /// A new . + public Image Mosaic(Image sec, Enums.Direction direction, int xref, int yref, int xsec, int ysec, out int dx0, int? hwindow = null, int? harea = null, int? mblend = null, int? bandno = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(hwindow), hwindow); + options.AddIfPresent(nameof(harea), harea); + options.AddIfPresent(nameof(mblend), mblend); + options.AddIfPresent(nameof(bandno), bandno); + + options.Add("dx0", true); + + var results = this.Call("mosaic", options, sec, direction, xref, yref, xsec, ysec) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + dx0 = opts?["dx0"] is int out1 ? out1 : 0; + + return finalResult; + } + + /// + /// Mosaic two images. + /// + /// + /// + /// using Image @out = ref.Mosaic(sec, direction, xref, yref, xsec, ysec, out var dx0, out var dy0, hwindow: int, harea: int, mblend: int, bandno: int); + /// + /// + /// Secondary image. + /// Horizontal or vertical mosaic. + /// Position of reference tie-point. + /// Position of reference tie-point. + /// Position of secondary tie-point. + /// Position of secondary tie-point. + /// Detected integer offset. + /// Detected integer offset. + /// Half window size. + /// Half area size. + /// Maximum blend size. + /// Band to search for features on. + /// A new . + public Image Mosaic(Image sec, Enums.Direction direction, int xref, int yref, int xsec, int ysec, out int dx0, out int dy0, int? hwindow = null, int? harea = null, int? mblend = null, int? bandno = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(hwindow), hwindow); + options.AddIfPresent(nameof(harea), harea); + options.AddIfPresent(nameof(mblend), mblend); + options.AddIfPresent(nameof(bandno), bandno); + + options.Add("dx0", true); + options.Add("dy0", true); + + var results = this.Call("mosaic", options, sec, direction, xref, yref, xsec, ysec) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + dx0 = opts?["dx0"] is int out1 ? out1 : 0; + dy0 = opts?["dy0"] is int out2 ? out2 : 0; + + return finalResult; + } + + /// + /// Mosaic two images. + /// + /// + /// + /// using Image @out = ref.Mosaic(sec, direction, xref, yref, xsec, ysec, out var dx0, out var dy0, out var scale1, hwindow: int, harea: int, mblend: int, bandno: int); + /// + /// + /// Secondary image. + /// Horizontal or vertical mosaic. + /// Position of reference tie-point. + /// Position of reference tie-point. + /// Position of secondary tie-point. + /// Position of secondary tie-point. + /// Detected integer offset. + /// Detected integer offset. + /// Detected scale. + /// Half window size. + /// Half area size. + /// Maximum blend size. + /// Band to search for features on. + /// A new . + public Image Mosaic(Image sec, Enums.Direction direction, int xref, int yref, int xsec, int ysec, out int dx0, out int dy0, out double scale1, int? hwindow = null, int? harea = null, int? mblend = null, int? bandno = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(hwindow), hwindow); + options.AddIfPresent(nameof(harea), harea); + options.AddIfPresent(nameof(mblend), mblend); + options.AddIfPresent(nameof(bandno), bandno); + + options.Add("dx0", true); + options.Add("dy0", true); + options.Add("scale1", true); + + var results = this.Call("mosaic", options, sec, direction, xref, yref, xsec, ysec) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + dx0 = opts?["dx0"] is int out1 ? out1 : 0; + dy0 = opts?["dy0"] is int out2 ? out2 : 0; + scale1 = opts?["scale1"] is double out3 ? out3 : 0d; + + return finalResult; + } + + /// + /// Mosaic two images. + /// + /// + /// + /// using Image @out = ref.Mosaic(sec, direction, xref, yref, xsec, ysec, out var dx0, out var dy0, out var scale1, out var angle1, hwindow: int, harea: int, mblend: int, bandno: int); + /// + /// + /// Secondary image. + /// Horizontal or vertical mosaic. + /// Position of reference tie-point. + /// Position of reference tie-point. + /// Position of secondary tie-point. + /// Position of secondary tie-point. + /// Detected integer offset. + /// Detected integer offset. + /// Detected scale. + /// Detected rotation. + /// Half window size. + /// Half area size. + /// Maximum blend size. + /// Band to search for features on. + /// A new . + public Image Mosaic(Image sec, Enums.Direction direction, int xref, int yref, int xsec, int ysec, out int dx0, out int dy0, out double scale1, out double angle1, int? hwindow = null, int? harea = null, int? mblend = null, int? bandno = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(hwindow), hwindow); + options.AddIfPresent(nameof(harea), harea); + options.AddIfPresent(nameof(mblend), mblend); + options.AddIfPresent(nameof(bandno), bandno); + + options.Add("dx0", true); + options.Add("dy0", true); + options.Add("scale1", true); + options.Add("angle1", true); + + var results = this.Call("mosaic", options, sec, direction, xref, yref, xsec, ysec) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + dx0 = opts?["dx0"] is int out1 ? out1 : 0; + dy0 = opts?["dy0"] is int out2 ? out2 : 0; + scale1 = opts?["scale1"] is double out3 ? out3 : 0d; + angle1 = opts?["angle1"] is double out4 ? out4 : 0d; + + return finalResult; + } + + /// + /// Mosaic two images. + /// + /// + /// + /// using Image @out = ref.Mosaic(sec, direction, xref, yref, xsec, ysec, out var dx0, out var dy0, out var scale1, out var angle1, out var dy1, hwindow: int, harea: int, mblend: int, bandno: int); + /// + /// + /// Secondary image. + /// Horizontal or vertical mosaic. + /// Position of reference tie-point. + /// Position of reference tie-point. + /// Position of secondary tie-point. + /// Position of secondary tie-point. + /// Detected integer offset. + /// Detected integer offset. + /// Detected scale. + /// Detected rotation. + /// Detected first-order displacement. + /// Half window size. + /// Half area size. + /// Maximum blend size. + /// Band to search for features on. + /// A new . + public Image Mosaic(Image sec, Enums.Direction direction, int xref, int yref, int xsec, int ysec, out int dx0, out int dy0, out double scale1, out double angle1, out double dy1, int? hwindow = null, int? harea = null, int? mblend = null, int? bandno = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(hwindow), hwindow); + options.AddIfPresent(nameof(harea), harea); + options.AddIfPresent(nameof(mblend), mblend); + options.AddIfPresent(nameof(bandno), bandno); + + options.Add("dx0", true); + options.Add("dy0", true); + options.Add("scale1", true); + options.Add("angle1", true); + options.Add("dy1", true); + + var results = this.Call("mosaic", options, sec, direction, xref, yref, xsec, ysec) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + dx0 = opts?["dx0"] is int out1 ? out1 : 0; + dy0 = opts?["dy0"] is int out2 ? out2 : 0; + scale1 = opts?["scale1"] is double out3 ? out3 : 0d; + angle1 = opts?["angle1"] is double out4 ? out4 : 0d; + dy1 = opts?["dy1"] is double out5 ? out5 : 0d; + + return finalResult; + } + + /// + /// Mosaic two images. + /// + /// + /// + /// using Image @out = ref.Mosaic(sec, direction, xref, yref, xsec, ysec, out var dx0, out var dy0, out var scale1, out var angle1, out var dy1, out var dx1, hwindow: int, harea: int, mblend: int, bandno: int); + /// + /// + /// Secondary image. + /// Horizontal or vertical mosaic. + /// Position of reference tie-point. + /// Position of reference tie-point. + /// Position of secondary tie-point. + /// Position of secondary tie-point. + /// Detected integer offset. + /// Detected integer offset. + /// Detected scale. + /// Detected rotation. + /// Detected first-order displacement. + /// Detected first-order displacement. + /// Half window size. + /// Half area size. + /// Maximum blend size. + /// Band to search for features on. + /// A new . + public Image Mosaic(Image sec, Enums.Direction direction, int xref, int yref, int xsec, int ysec, out int dx0, out int dy0, out double scale1, out double angle1, out double dy1, out double dx1, int? hwindow = null, int? harea = null, int? mblend = null, int? bandno = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(hwindow), hwindow); + options.AddIfPresent(nameof(harea), harea); + options.AddIfPresent(nameof(mblend), mblend); + options.AddIfPresent(nameof(bandno), bandno); + + options.Add("dx0", true); + options.Add("dy0", true); + options.Add("scale1", true); + options.Add("angle1", true); + options.Add("dy1", true); + options.Add("dx1", true); + + var results = this.Call("mosaic", options, sec, direction, xref, yref, xsec, ysec) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + dx0 = opts?["dx0"] is int out1 ? out1 : 0; + dy0 = opts?["dy0"] is int out2 ? out2 : 0; + scale1 = opts?["scale1"] is double out3 ? out3 : 0d; + angle1 = opts?["angle1"] is double out4 ? out4 : 0d; + dy1 = opts?["dy1"] is double out5 ? out5 : 0d; + dx1 = opts?["dx1"] is double out6 ? out6 : 0d; + + return finalResult; + } + + /// + /// First-order mosaic of two images. + /// + /// + /// + /// using Image @out = ref.Mosaic1(sec, direction, xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, hwindow: int, harea: int, search: bool, interpolate: GObject, mblend: int); + /// + /// + /// Secondary image. + /// Horizontal or vertical mosaic. + /// Position of first reference tie-point. + /// Position of first reference tie-point. + /// Position of first secondary tie-point. + /// Position of first secondary tie-point. + /// Position of second reference tie-point. + /// Position of second reference tie-point. + /// Position of second secondary tie-point. + /// Position of second secondary tie-point. + /// Half window size. + /// Half area size. + /// Search to improve tie-points. + /// Interpolate pixels with this. + /// Maximum blend size. + /// A new . + public Image Mosaic1(Image sec, Enums.Direction direction, int xr1, int yr1, int xs1, int ys1, int xr2, int yr2, int xs2, int ys2, int? hwindow = null, int? harea = null, bool? search = null, GObject interpolate = null, int? mblend = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(hwindow), hwindow); + options.AddIfPresent(nameof(harea), harea); + options.AddIfPresent(nameof(search), search); + options.AddIfPresent(nameof(interpolate), interpolate); + options.AddIfPresent(nameof(mblend), mblend); + + return this.Call("mosaic1", options, sec, direction, xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2) as Image; + } + + /// + /// Pick most-significant byte from an image. + /// + /// + /// + /// using Image @out = in.Msb(band: int); + /// + /// + /// Band to msb. + /// A new . + public Image Msb(int? band = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(band), band); + + return this.Call("msb", options) as Image; + } + + /// + /// Multiply two images. + /// + /// + /// + /// using Image @out = left.Multiply(right); + /// + /// + /// Right-hand image argument. + /// A new . + public Image Multiply(Image right) + { + return this.Call("multiply", right) as Image; + } + + /// + /// Load NIfTI volume. + /// + /// + /// + /// using Image @out = NetVips.Image.Niftiload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Niftiload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("niftiload", options, filename) as Image; + } + + /// + /// Load NIfTI volume. + /// + /// + /// + /// using Image @out = NetVips.Image.Niftiload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Niftiload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("niftiload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load NIfTI volumes. + /// + /// + /// + /// using Image @out = NetVips.Image.NiftiloadSource(source, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image NiftiloadSource(Source source, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("niftiload_source", options, source) as Image; + } + + /// + /// Load NIfTI volumes. + /// + /// + /// + /// using Image @out = NetVips.Image.NiftiloadStream(stream, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image NiftiloadStream(Stream stream, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = NiftiloadSource(source, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load NIfTI volumes. + /// + /// + /// + /// using Image @out = NetVips.Image.NiftiloadSource(source, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image NiftiloadSource(Source source, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("niftiload_source", options, source) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load NIfTI volumes. + /// + /// + /// + /// using Image @out = NetVips.Image.NiftiloadStream(stream, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image NiftiloadStream(Stream stream, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = NiftiloadSource(source, out flags, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Save image to nifti file. + /// + /// + /// + /// in.Niftisave(filename, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Filename to save to. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Niftisave(string filename, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("niftisave", options, filename); + } + + /// + /// Load an OpenEXR image. + /// + /// + /// + /// using Image @out = NetVips.Image.Openexrload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Openexrload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("openexrload", options, filename) as Image; + } + + /// + /// Load an OpenEXR image. + /// + /// + /// + /// using Image @out = NetVips.Image.Openexrload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Openexrload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("openexrload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load file with OpenSlide. + /// + /// + /// + /// using Image @out = NetVips.Image.Openslideload(filename, level: int, autocrop: bool, associated: string, attachAssociated: bool, rgb: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Load this level from the file. + /// Crop to image bounds. + /// Load this associated image. + /// Attach all associated images. + /// Output RGB (not RGBA). + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Openslideload(string filename, int? level = null, bool? autocrop = null, string associated = null, bool? attachAssociated = null, bool? rgb = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(level), level); + options.AddIfPresent(nameof(autocrop), autocrop); + options.AddIfPresent(nameof(associated), associated); + options.AddIfPresent("attach_associated", attachAssociated); + options.AddIfPresent(nameof(rgb), rgb); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("openslideload", options, filename) as Image; + } + + /// + /// Load file with OpenSlide. + /// + /// + /// + /// using Image @out = NetVips.Image.Openslideload(filename, out var flags, level: int, autocrop: bool, associated: string, attachAssociated: bool, rgb: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// Load this level from the file. + /// Crop to image bounds. + /// Load this associated image. + /// Attach all associated images. + /// Output RGB (not RGBA). + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Openslideload(string filename, out Enums.ForeignFlags flags, int? level = null, bool? autocrop = null, string associated = null, bool? attachAssociated = null, bool? rgb = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(level), level); + options.AddIfPresent(nameof(autocrop), autocrop); + options.AddIfPresent(nameof(associated), associated); + options.AddIfPresent("attach_associated", attachAssociated); + options.AddIfPresent(nameof(rgb), rgb); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("openslideload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load source with OpenSlide. + /// + /// + /// + /// using Image @out = NetVips.Image.OpenslideloadSource(source, level: int, autocrop: bool, associated: string, attachAssociated: bool, rgb: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Load this level from the file. + /// Crop to image bounds. + /// Load this associated image. + /// Attach all associated images. + /// Output RGB (not RGBA). + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image OpenslideloadSource(Source source, int? level = null, bool? autocrop = null, string associated = null, bool? attachAssociated = null, bool? rgb = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(level), level); + options.AddIfPresent(nameof(autocrop), autocrop); + options.AddIfPresent(nameof(associated), associated); + options.AddIfPresent("attach_associated", attachAssociated); + options.AddIfPresent(nameof(rgb), rgb); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("openslideload_source", options, source) as Image; + } + + /// + /// Load stream with OpenSlide. + /// + /// + /// + /// using Image @out = NetVips.Image.OpenslideloadStream(stream, level: int, autocrop: bool, associated: string, attachAssociated: bool, rgb: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Load this level from the file. + /// Crop to image bounds. + /// Load this associated image. + /// Attach all associated images. + /// Output RGB (not RGBA). + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image OpenslideloadStream(Stream stream, int? level = null, bool? autocrop = null, string associated = null, bool? attachAssociated = null, bool? rgb = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = OpenslideloadSource(source, level, autocrop, associated, attachAssociated, rgb, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load source with OpenSlide. + /// + /// + /// + /// using Image @out = NetVips.Image.OpenslideloadSource(source, out var flags, level: int, autocrop: bool, associated: string, attachAssociated: bool, rgb: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Flags for this file. + /// Load this level from the file. + /// Crop to image bounds. + /// Load this associated image. + /// Attach all associated images. + /// Output RGB (not RGBA). + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image OpenslideloadSource(Source source, out Enums.ForeignFlags flags, int? level = null, bool? autocrop = null, string associated = null, bool? attachAssociated = null, bool? rgb = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(level), level); + options.AddIfPresent(nameof(autocrop), autocrop); + options.AddIfPresent(nameof(associated), associated); + options.AddIfPresent("attach_associated", attachAssociated); + options.AddIfPresent(nameof(rgb), rgb); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("openslideload_source", options, source) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load stream with OpenSlide. + /// + /// + /// + /// using Image @out = NetVips.Image.OpenslideloadStream(stream, out var flags, level: int, autocrop: bool, associated: string, attachAssociated: bool, rgb: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Flags for this file. + /// Load this level from the file. + /// Crop to image bounds. + /// Load this associated image. + /// Attach all associated images. + /// Output RGB (not RGBA). + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image OpenslideloadStream(Stream stream, out Enums.ForeignFlags flags, int? level = null, bool? autocrop = null, string associated = null, bool? attachAssociated = null, bool? rgb = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = OpenslideloadSource(source, out flags, level, autocrop, associated, attachAssociated, rgb, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load PDF from file. + /// + /// + /// + /// using Image @out = NetVips.Image.Pdfload(filename, page: int, n: int, dpi: double, scale: double, background: double[], password: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// First page to load. + /// Number of pages to load, -1 for all. + /// DPI to render at. + /// Factor to scale by. + /// Background colour. + /// Password to decrypt with. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Pdfload(string filename, int? page = null, int? n = null, double? dpi = null, double? scale = null, double[] background = null, string password = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(dpi), dpi); + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent(nameof(password), password); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("pdfload", options, filename) as Image; + } + + /// + /// Load PDF from file. + /// + /// + /// + /// using Image @out = NetVips.Image.Pdfload(filename, out var flags, page: int, n: int, dpi: double, scale: double, background: double[], password: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// First page to load. + /// Number of pages to load, -1 for all. + /// DPI to render at. + /// Factor to scale by. + /// Background colour. + /// Password to decrypt with. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Pdfload(string filename, out Enums.ForeignFlags flags, int? page = null, int? n = null, double? dpi = null, double? scale = null, double[] background = null, string password = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(dpi), dpi); + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent(nameof(password), password); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("pdfload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load PDF from buffer. + /// + /// + /// + /// using Image @out = NetVips.Image.PdfloadBuffer(buffer, page: int, n: int, dpi: double, scale: double, background: double[], password: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// First page to load. + /// Number of pages to load, -1 for all. + /// DPI to render at. + /// Factor to scale by. + /// Background colour. + /// Password to decrypt with. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image PdfloadBuffer(byte[] buffer, int? page = null, int? n = null, double? dpi = null, double? scale = null, double[] background = null, string password = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(dpi), dpi); + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent(nameof(password), password); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("pdfload_buffer", options, buffer) as Image; + } + + /// + /// Load PDF from buffer. + /// + /// + /// + /// using Image @out = NetVips.Image.PdfloadBuffer(buffer, out var flags, page: int, n: int, dpi: double, scale: double, background: double[], password: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Flags for this file. + /// First page to load. + /// Number of pages to load, -1 for all. + /// DPI to render at. + /// Factor to scale by. + /// Background colour. + /// Password to decrypt with. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image PdfloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, int? page = null, int? n = null, double? dpi = null, double? scale = null, double[] background = null, string password = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(dpi), dpi); + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent(nameof(password), password); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("pdfload_buffer", options, buffer) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load PDF from source. + /// + /// + /// + /// using Image @out = NetVips.Image.PdfloadSource(source, page: int, n: int, dpi: double, scale: double, background: double[], password: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// First page to load. + /// Number of pages to load, -1 for all. + /// DPI to render at. + /// Factor to scale by. + /// Background colour. + /// Password to decrypt with. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image PdfloadSource(Source source, int? page = null, int? n = null, double? dpi = null, double? scale = null, double[] background = null, string password = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(dpi), dpi); + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent(nameof(password), password); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("pdfload_source", options, source) as Image; + } + + /// + /// Load PDF from stream. + /// + /// + /// + /// using Image @out = NetVips.Image.PdfloadStream(stream, page: int, n: int, dpi: double, scale: double, background: double[], password: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// First page to load. + /// Number of pages to load, -1 for all. + /// DPI to render at. + /// Factor to scale by. + /// Background colour. + /// Password to decrypt with. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image PdfloadStream(Stream stream, int? page = null, int? n = null, double? dpi = null, double? scale = null, double[] background = null, string password = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = PdfloadSource(source, page, n, dpi, scale, background, password, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load PDF from source. + /// + /// + /// + /// using Image @out = NetVips.Image.PdfloadSource(source, out var flags, page: int, n: int, dpi: double, scale: double, background: double[], password: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Flags for this file. + /// First page to load. + /// Number of pages to load, -1 for all. + /// DPI to render at. + /// Factor to scale by. + /// Background colour. + /// Password to decrypt with. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image PdfloadSource(Source source, out Enums.ForeignFlags flags, int? page = null, int? n = null, double? dpi = null, double? scale = null, double[] background = null, string password = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(dpi), dpi); + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent(nameof(password), password); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("pdfload_source", options, source) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load PDF from stream. + /// + /// + /// + /// using Image @out = NetVips.Image.PdfloadStream(stream, out var flags, page: int, n: int, dpi: double, scale: double, background: double[], password: string, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Flags for this file. + /// First page to load. + /// Number of pages to load, -1 for all. + /// DPI to render at. + /// Factor to scale by. + /// Background colour. + /// Password to decrypt with. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image PdfloadStream(Stream stream, out Enums.ForeignFlags flags, int? page = null, int? n = null, double? dpi = null, double? scale = null, double[] background = null, string password = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = PdfloadSource(source, out flags, page, n, dpi, scale, background, password, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Find threshold for percent of pixels. + /// + /// + /// + /// int threshold = in.Percent(percent); + /// + /// + /// Percent of pixels. + /// A int. + public int Percent(double percent) + { + return this.Call("percent", percent) is int result ? result : 0; + } + + /// + /// Make a perlin noise image. + /// + /// + /// + /// using Image @out = NetVips.Image.Perlin(width, height, cellSize: int, uchar: bool, seed: int); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Size of Perlin cells. + /// Output an unsigned char image. + /// Random number seed. + /// A new . + public static Image Perlin(int width, int height, int? cellSize = null, bool? uchar = null, int? seed = null) + { + var options = new VOption(); + + options.AddIfPresent("cell_size", cellSize); + options.AddIfPresent(nameof(uchar), uchar); + options.AddIfPresent(nameof(seed), seed); + + return Operation.Call("perlin", options, width, height) as Image; + } + + /// + /// Calculate phase correlation. + /// + /// + /// + /// using Image @out = in.Phasecor(in2); + /// + /// + /// Second input image. + /// A new . + public Image Phasecor(Image in2) + { + return this.Call("phasecor", in2) as Image; + } + + /// + /// Load png from file. + /// + /// + /// + /// using Image @out = NetVips.Image.Pngload(filename, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Pngload(string filename, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("pngload", options, filename) as Image; + } + + /// + /// Load png from file. + /// + /// + /// + /// using Image @out = NetVips.Image.Pngload(filename, out var flags, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Pngload(string filename, out Enums.ForeignFlags flags, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("pngload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load png from buffer. + /// + /// + /// + /// using Image @out = NetVips.Image.PngloadBuffer(buffer, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image PngloadBuffer(byte[] buffer, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("pngload_buffer", options, buffer) as Image; + } + + /// + /// Load png from buffer. + /// + /// + /// + /// using Image @out = NetVips.Image.PngloadBuffer(buffer, out var flags, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Flags for this file. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image PngloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("pngload_buffer", options, buffer) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load png from source. + /// + /// + /// + /// using Image @out = NetVips.Image.PngloadSource(source, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image PngloadSource(Source source, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("pngload_source", options, source) as Image; + } + + /// + /// Load png from stream. + /// + /// + /// + /// using Image @out = NetVips.Image.PngloadStream(stream, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image PngloadStream(Stream stream, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = PngloadSource(source, unlimited, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load png from source. + /// + /// + /// + /// using Image @out = NetVips.Image.PngloadSource(source, out var flags, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Flags for this file. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image PngloadSource(Source source, out Enums.ForeignFlags flags, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("pngload_source", options, source) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load png from stream. + /// + /// + /// + /// using Image @out = NetVips.Image.PngloadStream(stream, out var flags, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Flags for this file. + /// Remove all denial of service limits. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image PngloadStream(Stream stream, out Enums.ForeignFlags flags, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = PngloadSource(source, out flags, unlimited, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Save image to file as PNG. + /// + /// + /// + /// in.Pngsave(filename, compression: int, interlace: bool, filter: Enums.ForeignPngFilter, palette: bool, q: int, dither: double, bitdepth: int, effort: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Filename to save to. + /// Compression factor. + /// Interlace image. + /// libspng row filter flag(s). + /// Quantise to 8bpp palette. + /// Quantisation quality. + /// Amount of dithering. + /// Write as a 1, 2, 4, 8 or 16 bit image. + /// Quantisation CPU effort. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Pngsave(string filename, int? compression = null, bool? interlace = null, Enums.ForeignPngFilter? filter = null, bool? palette = null, int? q = null, double? dither = null, int? bitdepth = null, int? effort = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(compression), compression); + options.AddIfPresent(nameof(interlace), interlace); + options.AddIfPresent(nameof(filter), filter); + options.AddIfPresent(nameof(palette), palette); + options.AddIfPresent("Q", q); + options.AddIfPresent(nameof(dither), dither); + options.AddIfPresent(nameof(bitdepth), bitdepth); + options.AddIfPresent(nameof(effort), effort); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("pngsave", options, filename); + } + + /// + /// Save image to buffer as PNG. + /// + /// + /// + /// byte[] buffer = in.PngsaveBuffer(compression: int, interlace: bool, filter: Enums.ForeignPngFilter, palette: bool, q: int, dither: double, bitdepth: int, effort: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Compression factor. + /// Interlace image. + /// libspng row filter flag(s). + /// Quantise to 8bpp palette. + /// Quantisation quality. + /// Amount of dithering. + /// Write as a 1, 2, 4, 8 or 16 bit image. + /// Quantisation CPU effort. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + /// An array of bytes. + public byte[] PngsaveBuffer(int? compression = null, bool? interlace = null, Enums.ForeignPngFilter? filter = null, bool? palette = null, int? q = null, double? dither = null, int? bitdepth = null, int? effort = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(compression), compression); + options.AddIfPresent(nameof(interlace), interlace); + options.AddIfPresent(nameof(filter), filter); + options.AddIfPresent(nameof(palette), palette); + options.AddIfPresent("Q", q); + options.AddIfPresent(nameof(dither), dither); + options.AddIfPresent(nameof(bitdepth), bitdepth); + options.AddIfPresent(nameof(effort), effort); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + return this.Call("pngsave_buffer", options) as byte[]; + } + + /// + /// Save image to target as PNG. + /// + /// + /// + /// in.PngsaveTarget(target, compression: int, interlace: bool, filter: Enums.ForeignPngFilter, palette: bool, q: int, dither: double, bitdepth: int, effort: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Target to save to. + /// Compression factor. + /// Interlace image. + /// libspng row filter flag(s). + /// Quantise to 8bpp palette. + /// Quantisation quality. + /// Amount of dithering. + /// Write as a 1, 2, 4, 8 or 16 bit image. + /// Quantisation CPU effort. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void PngsaveTarget(Target target, int? compression = null, bool? interlace = null, Enums.ForeignPngFilter? filter = null, bool? palette = null, int? q = null, double? dither = null, int? bitdepth = null, int? effort = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(compression), compression); + options.AddIfPresent(nameof(interlace), interlace); + options.AddIfPresent(nameof(filter), filter); + options.AddIfPresent(nameof(palette), palette); + options.AddIfPresent("Q", q); + options.AddIfPresent(nameof(dither), dither); + options.AddIfPresent(nameof(bitdepth), bitdepth); + options.AddIfPresent(nameof(effort), effort); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("pngsave_target", options, target); + } + + /// + /// Save image to stream as PNG. + /// + /// + /// + /// in.PngsaveStream(stream, compression: int, interlace: bool, filter: Enums.ForeignPngFilter, palette: bool, q: int, dither: double, bitdepth: int, effort: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Stream to save to. + /// Compression factor. + /// Interlace image. + /// libspng row filter flag(s). + /// Quantise to 8bpp palette. + /// Quantisation quality. + /// Amount of dithering. + /// Write as a 1, 2, 4, 8 or 16 bit image. + /// Quantisation CPU effort. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void PngsaveStream(Stream stream, int? compression = null, bool? interlace = null, Enums.ForeignPngFilter? filter = null, bool? palette = null, int? q = null, double? dither = null, int? bitdepth = null, int? effort = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + using var target = TargetStream.NewFromStream(stream); + PngsaveTarget(target, compression, interlace, filter, palette, q, dither, bitdepth, effort, keep, background, pageHeight, profile); + } + + /// + /// Load ppm from file. + /// + /// + /// + /// using Image @out = NetVips.Image.Ppmload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Ppmload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("ppmload", options, filename) as Image; + } + + /// + /// Load ppm from file. + /// + /// + /// + /// using Image @out = NetVips.Image.Ppmload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Ppmload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("ppmload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load ppm base class. + /// + /// + /// + /// using Image @out = NetVips.Image.PpmloadSource(source, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image PpmloadSource(Source source, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("ppmload_source", options, source) as Image; + } + + /// + /// Load ppm base class. + /// + /// + /// + /// using Image @out = NetVips.Image.PpmloadStream(stream, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image PpmloadStream(Stream stream, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = PpmloadSource(source, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load ppm base class. + /// + /// + /// + /// using Image @out = NetVips.Image.PpmloadSource(source, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image PpmloadSource(Source source, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("ppmload_source", options, source) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load ppm base class. + /// + /// + /// + /// using Image @out = NetVips.Image.PpmloadStream(stream, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image PpmloadStream(Stream stream, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = PpmloadSource(source, out flags, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Save image to ppm file. + /// + /// + /// + /// in.Ppmsave(filename, format: Enums.ForeignPpmFormat, ascii: bool, bitdepth: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Filename to save to. + /// Format to save in. + /// Save as ascii. + /// Set to 1 to write as a 1 bit image. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Ppmsave(string filename, Enums.ForeignPpmFormat? format = null, bool? ascii = null, int? bitdepth = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(format), format); + options.AddIfPresent(nameof(ascii), ascii); + options.AddIfPresent(nameof(bitdepth), bitdepth); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("ppmsave", options, filename); + } + + /// + /// Save to ppm. + /// + /// + /// + /// in.PpmsaveTarget(target, format: Enums.ForeignPpmFormat, ascii: bool, bitdepth: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Target to save to. + /// Format to save in. + /// Save as ascii. + /// Set to 1 to write as a 1 bit image. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void PpmsaveTarget(Target target, Enums.ForeignPpmFormat? format = null, bool? ascii = null, int? bitdepth = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(format), format); + options.AddIfPresent(nameof(ascii), ascii); + options.AddIfPresent(nameof(bitdepth), bitdepth); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("ppmsave_target", options, target); + } + + /// + /// Save to ppm. + /// + /// + /// + /// in.PpmsaveStream(stream, format: Enums.ForeignPpmFormat, ascii: bool, bitdepth: int, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Stream to save to. + /// Format to save in. + /// Save as ascii. + /// Set to 1 to write as a 1 bit image. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void PpmsaveStream(Stream stream, Enums.ForeignPpmFormat? format = null, bool? ascii = null, int? bitdepth = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + using var target = TargetStream.NewFromStream(stream); + PpmsaveTarget(target, format, ascii, bitdepth, keep, background, pageHeight, profile); + } + + /// + /// Premultiply image alpha. + /// + /// + /// + /// using Image @out = in.Premultiply(maxAlpha: double); + /// + /// + /// Maximum value of alpha channel. + /// A new . + public Image Premultiply(double? maxAlpha = null) + { + var options = new VOption(); + + options.AddIfPresent("max_alpha", maxAlpha); + + return this.Call("premultiply", options) as Image; + } + + /// + /// Prewitt edge detector. + /// + /// + /// + /// using Image @out = in.Prewitt(); + /// + /// + /// A new . + public Image Prewitt() + { + return this.Call("prewitt") as Image; + } + + /// + /// Find image profiles. + /// + /// + /// + /// var output = in.Profile(); + /// + /// + /// An array of objects. + public object[] Profile() + { + return this.Call("profile") as object[]; + } + + /// + /// Load named ICC profile. + /// + /// + /// + /// byte[] profile = NetVips.Image.ProfileLoad(name); + /// + /// + /// Profile name. + /// An array of bytes. + public static byte[] ProfileLoad(string name) + { + return Operation.Call("profile_load", name) as byte[]; + } + + /// + /// Find image projections. + /// + /// + /// + /// var output = in.Project(); + /// + /// + /// An array of objects. + public object[] Project() + { + return this.Call("project") as object[]; + } + + /// + /// Resample an image with a quadratic transform. + /// + /// + /// + /// using Image @out = in.Quadratic(coeff, interpolate: GObject); + /// + /// + /// Coefficient matrix. + /// Interpolate values with this. + /// A new . + public Image Quadratic(Image coeff, GObject interpolate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(interpolate), interpolate); + + return this.Call("quadratic", options, coeff) as Image; + } + + /// + /// Unpack Radiance coding to float RGB. + /// + /// + /// + /// using Image @out = in.Rad2float(); + /// + /// + /// A new . + public Image Rad2float() + { + return this.Call("rad2float") as Image; + } + + /// + /// Load a Radiance image from a file. + /// + /// + /// + /// using Image @out = NetVips.Image.Radload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Radload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("radload", options, filename) as Image; + } + + /// + /// Load a Radiance image from a file. + /// + /// + /// + /// using Image @out = NetVips.Image.Radload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Radload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("radload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load rad from buffer. + /// + /// + /// + /// using Image @out = NetVips.Image.RadloadBuffer(buffer, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image RadloadBuffer(byte[] buffer, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("radload_buffer", options, buffer) as Image; + } + + /// + /// Load rad from buffer. + /// + /// + /// + /// using Image @out = NetVips.Image.RadloadBuffer(buffer, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image RadloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("radload_buffer", options, buffer) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load rad from source. + /// + /// + /// + /// using Image @out = NetVips.Image.RadloadSource(source, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image RadloadSource(Source source, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("radload_source", options, source) as Image; + } + + /// + /// Load rad from stream. + /// + /// + /// + /// using Image @out = NetVips.Image.RadloadStream(stream, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image RadloadStream(Stream stream, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = RadloadSource(source, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load rad from source. + /// + /// + /// + /// using Image @out = NetVips.Image.RadloadSource(source, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image RadloadSource(Source source, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("radload_source", options, source) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load rad from stream. + /// + /// + /// + /// using Image @out = NetVips.Image.RadloadStream(stream, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image RadloadStream(Stream stream, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = RadloadSource(source, out flags, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Save image to Radiance file. + /// + /// + /// + /// in.Radsave(filename, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Filename to save to. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Radsave(string filename, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("radsave", options, filename); + } + + /// + /// Save image to Radiance buffer. + /// + /// + /// + /// byte[] buffer = in.RadsaveBuffer(keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + /// An array of bytes. + public byte[] RadsaveBuffer(Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + return this.Call("radsave_buffer", options) as byte[]; + } + + /// + /// Save image to Radiance target. + /// + /// + /// + /// in.RadsaveTarget(target, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Target to save to. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void RadsaveTarget(Target target, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("radsave_target", options, target); + } + + /// + /// Save image to Radiance stream. + /// + /// + /// + /// in.RadsaveStream(stream, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Stream to save to. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void RadsaveStream(Stream stream, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + using var target = TargetStream.NewFromStream(stream); + RadsaveTarget(target, keep, background, pageHeight, profile); + } + + /// + /// Rank filter. + /// + /// + /// + /// using Image @out = in.Rank(width, height, index); + /// + /// + /// Window width in pixels. + /// Window height in pixels. + /// Select pixel at index. + /// A new . + public Image Rank(int width, int height, int index) + { + return this.Call("rank", width, height, index) as Image; + } + + /// + /// Load raw data from a file. + /// + /// + /// + /// using Image @out = NetVips.Image.Rawload(filename, width, height, bands, offset: ulong, format: Enums.BandFormat, interpretation: Enums.Interpretation, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Image width in pixels. + /// Image height in pixels. + /// Number of bands in image. + /// Offset in bytes from start of file. + /// Pixel format in image. + /// Pixel interpretation. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Rawload(string filename, int width, int height, int bands, ulong? offset = null, Enums.BandFormat? format = null, Enums.Interpretation? interpretation = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(offset), offset); + options.AddIfPresent(nameof(format), format); + options.AddIfPresent(nameof(interpretation), interpretation); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("rawload", options, filename, width, height, bands) as Image; + } + + /// + /// Load raw data from a file. + /// + /// + /// + /// using Image @out = NetVips.Image.Rawload(filename, width, height, bands, out var flags, offset: ulong, format: Enums.BandFormat, interpretation: Enums.Interpretation, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Image width in pixels. + /// Image height in pixels. + /// Number of bands in image. + /// Flags for this file. + /// Offset in bytes from start of file. + /// Pixel format in image. + /// Pixel interpretation. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Rawload(string filename, int width, int height, int bands, out Enums.ForeignFlags flags, ulong? offset = null, Enums.BandFormat? format = null, Enums.Interpretation? interpretation = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(offset), offset); + options.AddIfPresent(nameof(format), format); + options.AddIfPresent(nameof(interpretation), interpretation); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("rawload", options, filename, width, height, bands) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Save image to raw file. + /// + /// + /// + /// in.Rawsave(filename, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Filename to save to. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Rawsave(string filename, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("rawsave", options, filename); + } + + /// + /// Write raw image to file descriptor. + /// + /// + /// + /// in.RawsaveFd(fd, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// File descriptor to write to. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void RawsaveFd(int fd, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("rawsave_fd", options, fd); + } + + /// + /// Linear recombination with matrix. + /// + /// + /// + /// using Image @out = in.Recomb(m); + /// + /// + /// Matrix of coefficients. + /// A new . + public Image Recomb(Image m) + { + return this.Call("recomb", m) as Image; + } + + /// + /// Reduce an image. + /// + /// + /// + /// using Image @out = in.Reduce(hshrink, vshrink, kernel: Enums.Kernel, gap: double); + /// + /// + /// Horizontal shrink factor. + /// Vertical shrink factor. + /// Resampling kernel. + /// Reducing gap. + /// A new . + public Image Reduce(double hshrink, double vshrink, Enums.Kernel? kernel = null, double? gap = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(kernel), kernel); + options.AddIfPresent(nameof(gap), gap); + + return this.Call("reduce", options, hshrink, vshrink) as Image; + } + + /// + /// Shrink an image horizontally. + /// + /// + /// + /// using Image @out = in.Reduceh(hshrink, kernel: Enums.Kernel, gap: double); + /// + /// + /// Horizontal shrink factor. + /// Resampling kernel. + /// Reducing gap. + /// A new . + public Image Reduceh(double hshrink, Enums.Kernel? kernel = null, double? gap = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(kernel), kernel); + options.AddIfPresent(nameof(gap), gap); + + return this.Call("reduceh", options, hshrink) as Image; + } + + /// + /// Shrink an image vertically. + /// + /// + /// + /// using Image @out = in.Reducev(vshrink, kernel: Enums.Kernel, gap: double); + /// + /// + /// Vertical shrink factor. + /// Resampling kernel. + /// Reducing gap. + /// A new . + public Image Reducev(double vshrink, Enums.Kernel? kernel = null, double? gap = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(kernel), kernel); + options.AddIfPresent(nameof(gap), gap); + + return this.Call("reducev", options, vshrink) as Image; + } + + /// + /// Relational operation on two images. + /// + /// + /// + /// using Image @out = left.Relational(right, relational); + /// + /// + /// Right-hand image argument. + /// Relational to perform. + /// A new . + public Image Relational(Image right, Enums.OperationRelational relational) + { + return this.Call("relational", right, relational) as Image; + } + + /// + /// Relational operations against a constant. + /// + /// + /// + /// using Image @out = in.RelationalConst(relational, c); + /// + /// + /// Relational to perform. + /// Array of constants. + /// A new . + public Image RelationalConst(Enums.OperationRelational relational, double[] c) + { + return this.Call("relational_const", relational, c) as Image; + } + + /// + /// Remainder after integer division of two images. + /// + /// + /// + /// using Image @out = left.Remainder(right); + /// + /// + /// Right-hand image argument. + /// A new . + public Image Remainder(Image right) + { + return this.Call("remainder", right) as Image; + } + + /// + /// Remainder after integer division of an image and a constant. + /// + /// + /// + /// using Image @out = in.RemainderConst(c); + /// + /// + /// Array of constants. + /// A new . + public Image RemainderConst(double[] c) + { + return this.Call("remainder_const", c) as Image; + } + + /// + /// Replicate an image. + /// + /// + /// + /// using Image @out = in.Replicate(across, down); + /// + /// + /// Repeat this many times horizontally. + /// Repeat this many times vertically. + /// A new . + public Image Replicate(int across, int down) + { + return this.Call("replicate", across, down) as Image; + } + + /// + /// Resize an image. + /// + /// + /// + /// using Image @out = in.Resize(scale, kernel: Enums.Kernel, gap: double, vscale: double); + /// + /// + /// Scale image by this factor. + /// Resampling kernel. + /// Reducing gap. + /// Vertical scale image by this factor. + /// A new . + public Image Resize(double scale, Enums.Kernel? kernel = null, double? gap = null, double? vscale = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(kernel), kernel); + options.AddIfPresent(nameof(gap), gap); + options.AddIfPresent(nameof(vscale), vscale); + + return this.Call("resize", options, scale) as Image; + } + + /// + /// Rotate an image. + /// + /// + /// + /// using Image @out = in.Rot(angle); + /// + /// + /// Angle to rotate image. + /// A new . + public Image Rot(Enums.Angle angle) + { + return this.Call("rot", angle) as Image; + } + + /// + /// Rotate an image. + /// + /// + /// + /// using Image @out = in.Rot45(angle: Enums.Angle45); + /// + /// + /// Angle to rotate image. + /// A new . + public Image Rot45(Enums.Angle45? angle = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(angle), angle); + + return this.Call("rot45", options) as Image; + } + + /// + /// Rotate an image by a number of degrees. + /// + /// + /// + /// using Image @out = in.Rotate(angle, interpolate: GObject, background: double[], odx: double, ody: double, idx: double, idy: double); + /// + /// + /// Rotate anticlockwise by this many degrees. + /// Interpolate pixels with this. + /// Background value. + /// Horizontal output displacement. + /// Vertical output displacement. + /// Horizontal input displacement. + /// Vertical input displacement. + /// A new . + public Image Rotate(double angle, GObject interpolate = null, double[] background = null, double? odx = null, double? ody = null, double? idx = null, double? idy = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(interpolate), interpolate); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent(nameof(odx), odx); + options.AddIfPresent(nameof(ody), ody); + options.AddIfPresent(nameof(idx), idx); + options.AddIfPresent(nameof(idy), idy); + + return this.Call("rotate", options, angle) as Image; + } + + /// + /// Perform a round function on an image. + /// + /// + /// + /// using Image @out = in.Round(round); + /// + /// + /// Rounding operation to perform. + /// A new . + public Image Round(Enums.OperationRound round) + { + return this.Call("round", round) as Image; + } + + /// + /// Scharr edge detector. + /// + /// + /// + /// using Image @out = in.Scharr(); + /// + /// + /// A new . + public Image Scharr() + { + return this.Call("scharr") as Image; + } + + /// + /// Convert scRGB to BW. + /// + /// + /// + /// using Image @out = in.ScRGB2BW(depth: int); + /// + /// + /// Output device space depth in bits. + /// A new . + public Image ScRGB2BW(int? depth = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(depth), depth); + + return this.Call("scRGB2BW", options) as Image; + } + + /// + /// Convert an scRGB image to sRGB. + /// + /// + /// + /// using Image @out = in.ScRGB2sRGB(depth: int); + /// + /// + /// Output device space depth in bits. + /// A new . + public Image ScRGB2sRGB(int? depth = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(depth), depth); + + return this.Call("scRGB2sRGB", options) as Image; + } + + /// + /// Transform scRGB to XYZ. + /// + /// + /// + /// using Image @out = in.ScRGB2XYZ(); + /// + /// + /// A new . + public Image ScRGB2XYZ() + { + return this.Call("scRGB2XYZ") as Image; + } + + /// + /// Check sequential access. + /// + /// + /// + /// using Image @out = in.Sequential(tileHeight: int); + /// + /// + /// Tile height in pixels. + /// A new . + public Image Sequential(int? tileHeight = null) + { + var options = new VOption(); + + options.AddIfPresent("tile_height", tileHeight); + + return this.Call("sequential", options) as Image; + } + + /// + /// Unsharp masking for print. + /// + /// + /// + /// using Image @out = in.Sharpen(sigma: double, x1: double, y2: double, y3: double, m1: double, m2: double); + /// + /// + /// Sigma of Gaussian. + /// Flat/jaggy threshold. + /// Maximum brightening. + /// Maximum darkening. + /// Slope for flat areas. + /// Slope for jaggy areas. + /// A new . + public Image Sharpen(double? sigma = null, double? x1 = null, double? y2 = null, double? y3 = null, double? m1 = null, double? m2 = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(sigma), sigma); + options.AddIfPresent(nameof(x1), x1); + options.AddIfPresent(nameof(y2), y2); + options.AddIfPresent(nameof(y3), y3); + options.AddIfPresent(nameof(m1), m1); + options.AddIfPresent(nameof(m2), m2); + + return this.Call("sharpen", options) as Image; + } + + /// + /// Shrink an image. + /// + /// + /// + /// using Image @out = in.Shrink(hshrink, vshrink, ceil: bool); + /// + /// + /// Horizontal shrink factor. + /// Vertical shrink factor. + /// Round-up output dimensions. + /// A new . + public Image Shrink(double hshrink, double vshrink, bool? ceil = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(ceil), ceil); + + return this.Call("shrink", options, hshrink, vshrink) as Image; + } + + /// + /// Shrink an image horizontally. + /// + /// + /// + /// using Image @out = in.Shrinkh(hshrink, ceil: bool); + /// + /// + /// Horizontal shrink factor. + /// Round-up output dimensions. + /// A new . + public Image Shrinkh(int hshrink, bool? ceil = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(ceil), ceil); + + return this.Call("shrinkh", options, hshrink) as Image; + } + + /// + /// Shrink an image vertically. + /// + /// + /// + /// using Image @out = in.Shrinkv(vshrink, ceil: bool); + /// + /// + /// Vertical shrink factor. + /// Round-up output dimensions. + /// A new . + public Image Shrinkv(int vshrink, bool? ceil = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(ceil), ceil); + + return this.Call("shrinkv", options, vshrink) as Image; + } + + /// + /// Unit vector of pixel. + /// + /// + /// + /// using Image @out = in.Sign(); + /// + /// + /// A new . + public Image Sign() + { + return this.Call("sign") as Image; + } + + /// + /// Similarity transform of an image. + /// + /// + /// + /// using Image @out = in.Similarity(scale: double, angle: double, interpolate: GObject, background: double[], odx: double, ody: double, idx: double, idy: double); + /// + /// + /// Scale by this factor. + /// Rotate anticlockwise by this many degrees. + /// Interpolate pixels with this. + /// Background value. + /// Horizontal output displacement. + /// Vertical output displacement. + /// Horizontal input displacement. + /// Vertical input displacement. + /// A new . + public Image Similarity(double? scale = null, double? angle = null, GObject interpolate = null, double[] background = null, double? odx = null, double? ody = null, double? idx = null, double? idy = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(angle), angle); + options.AddIfPresent(nameof(interpolate), interpolate); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent(nameof(odx), odx); + options.AddIfPresent(nameof(ody), ody); + options.AddIfPresent(nameof(idx), idx); + options.AddIfPresent(nameof(idy), idy); + + return this.Call("similarity", options) as Image; + } + + /// + /// Make a 2D sine wave. + /// + /// + /// + /// using Image @out = NetVips.Image.Sines(width, height, uchar: bool, hfreq: double, vfreq: double); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Output an unsigned char image. + /// Horizontal spatial frequency. + /// Vertical spatial frequency. + /// A new . + public static Image Sines(int width, int height, bool? uchar = null, double? hfreq = null, double? vfreq = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(uchar), uchar); + options.AddIfPresent(nameof(hfreq), hfreq); + options.AddIfPresent(nameof(vfreq), vfreq); + + return Operation.Call("sines", options, width, height) as Image; + } + + /// + /// Extract an area from an image. + /// + /// + /// + /// using Image @out = input.Smartcrop(width, height, interesting: Enums.Interesting, premultiplied: bool); + /// + /// + /// Width of extract area. + /// Height of extract area. + /// How to measure interestingness. + /// Input image already has premultiplied alpha. + /// A new . + public Image Smartcrop(int width, int height, Enums.Interesting? interesting = null, bool? premultiplied = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(interesting), interesting); + options.AddIfPresent(nameof(premultiplied), premultiplied); + + return this.Call("smartcrop", options, width, height) as Image; + } + + /// + /// Extract an area from an image. + /// + /// + /// + /// using Image @out = input.Smartcrop(width, height, out var attentionX, interesting: Enums.Interesting, premultiplied: bool); + /// + /// + /// Width of extract area. + /// Height of extract area. + /// Horizontal position of attention centre. + /// How to measure interestingness. + /// Input image already has premultiplied alpha. + /// A new . + public Image Smartcrop(int width, int height, out int attentionX, Enums.Interesting? interesting = null, bool? premultiplied = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(interesting), interesting); + options.AddIfPresent(nameof(premultiplied), premultiplied); + + options.Add("attention_x", true); + + var results = this.Call("smartcrop", options, width, height) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + attentionX = opts?["attention_x"] is int out1 ? out1 : 0; + + return finalResult; + } + + /// + /// Extract an area from an image. + /// + /// + /// + /// using Image @out = input.Smartcrop(width, height, out var attentionX, out var attentionY, interesting: Enums.Interesting, premultiplied: bool); + /// + /// + /// Width of extract area. + /// Height of extract area. + /// Horizontal position of attention centre. + /// Vertical position of attention centre. + /// How to measure interestingness. + /// Input image already has premultiplied alpha. + /// A new . + public Image Smartcrop(int width, int height, out int attentionX, out int attentionY, Enums.Interesting? interesting = null, bool? premultiplied = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(interesting), interesting); + options.AddIfPresent(nameof(premultiplied), premultiplied); + + options.Add("attention_x", true); + options.Add("attention_y", true); + + var results = this.Call("smartcrop", options, width, height) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + attentionX = opts?["attention_x"] is int out1 ? out1 : 0; + attentionY = opts?["attention_y"] is int out2 ? out2 : 0; + + return finalResult; + } + + /// + /// Sobel edge detector. + /// + /// + /// + /// using Image @out = in.Sobel(); + /// + /// + /// A new . + public Image Sobel() + { + return this.Call("sobel") as Image; + } + + /// + /// Spatial correlation. + /// + /// + /// + /// using Image @out = in.Spcor(@ref); + /// + /// + /// Input reference image. + /// A new . + public Image Spcor(Image @ref) + { + return this.Call("spcor", @ref) as Image; + } + + /// + /// Make displayable power spectrum. + /// + /// + /// + /// using Image @out = in.Spectrum(); + /// + /// + /// A new . + public Image Spectrum() + { + return this.Call("spectrum") as Image; + } + + /// + /// Transform sRGB to HSV. + /// + /// + /// + /// using Image @out = in.SRGB2HSV(); + /// + /// + /// A new . + public Image SRGB2HSV() + { + return this.Call("sRGB2HSV") as Image; + } + + /// + /// Convert an sRGB image to scRGB. + /// + /// + /// + /// using Image @out = in.SRGB2scRGB(); + /// + /// + /// A new . + public Image SRGB2scRGB() + { + return this.Call("sRGB2scRGB") as Image; + } + + /// + /// Find many image stats. + /// + /// + /// + /// using Image @out = in.Stats(); + /// + /// + /// A new . + public Image Stats() + { + return this.Call("stats") as Image; + } + + /// + /// Statistical difference. + /// + /// + /// + /// using Image @out = in.Stdif(width, height, s0: double, b: double, m0: double, a: double); + /// + /// + /// Window width in pixels. + /// Window height in pixels. + /// New deviation. + /// Weight of new deviation. + /// New mean. + /// Weight of new mean. + /// A new . + public Image Stdif(int width, int height, double? s0 = null, double? b = null, double? m0 = null, double? a = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(s0), s0); + options.AddIfPresent(nameof(b), b); + options.AddIfPresent(nameof(m0), m0); + options.AddIfPresent(nameof(a), a); + + return this.Call("stdif", options, width, height) as Image; + } + + /// + /// Subsample an image. + /// + /// + /// + /// using Image @out = input.Subsample(xfac, yfac, point: bool); + /// + /// + /// Horizontal subsample factor. + /// Vertical subsample factor. + /// Point sample. + /// A new . + public Image Subsample(int xfac, int yfac, bool? point = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(point), point); + + return this.Call("subsample", options, xfac, yfac) as Image; + } + + /// + /// Subtract two images. + /// + /// + /// + /// using Image @out = left.Subtract(right); + /// + /// + /// Right-hand image argument. + /// A new . + public Image Subtract(Image right) + { + return this.Call("subtract", right) as Image; + } + + /// + /// Sum an array of images. + /// + /// + /// + /// using Image @out = NetVips.Image.Sum(@in); + /// + /// + /// Array of input images. + /// A new . + public static Image Sum(params Image[] @in) + { + return Operation.Call("sum", new object[] { @in }) as Image; + } + + /// + /// Load SVG with rsvg. + /// + /// + /// + /// using Image @out = NetVips.Image.Svgload(filename, dpi: double, scale: double, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Render at this DPI. + /// Scale output by this factor. + /// Allow SVG of any size. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Svgload(string filename, double? dpi = null, double? scale = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(dpi), dpi); + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("svgload", options, filename) as Image; + } + + /// + /// Load SVG with rsvg. + /// + /// + /// + /// using Image @out = NetVips.Image.Svgload(filename, out var flags, dpi: double, scale: double, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// Render at this DPI. + /// Scale output by this factor. + /// Allow SVG of any size. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Svgload(string filename, out Enums.ForeignFlags flags, double? dpi = null, double? scale = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(dpi), dpi); + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("svgload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load SVG with rsvg. + /// + /// + /// + /// using Image @out = NetVips.Image.SvgloadBuffer(buffer, dpi: double, scale: double, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Render at this DPI. + /// Scale output by this factor. + /// Allow SVG of any size. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image SvgloadBuffer(byte[] buffer, double? dpi = null, double? scale = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(dpi), dpi); + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("svgload_buffer", options, buffer) as Image; + } + + /// + /// Load SVG with rsvg. + /// + /// + /// + /// using Image @out = NetVips.Image.SvgloadBuffer(buffer, out var flags, dpi: double, scale: double, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Flags for this file. + /// Render at this DPI. + /// Scale output by this factor. + /// Allow SVG of any size. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image SvgloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, double? dpi = null, double? scale = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(dpi), dpi); + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("svgload_buffer", options, buffer) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load svg from source. + /// + /// + /// + /// using Image @out = NetVips.Image.SvgloadSource(source, dpi: double, scale: double, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Render at this DPI. + /// Scale output by this factor. + /// Allow SVG of any size. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image SvgloadSource(Source source, double? dpi = null, double? scale = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(dpi), dpi); + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("svgload_source", options, source) as Image; + } + + /// + /// Load svg from stream. + /// + /// + /// + /// using Image @out = NetVips.Image.SvgloadStream(stream, dpi: double, scale: double, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Render at this DPI. + /// Scale output by this factor. + /// Allow SVG of any size. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image SvgloadStream(Stream stream, double? dpi = null, double? scale = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = SvgloadSource(source, dpi, scale, unlimited, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load svg from source. + /// + /// + /// + /// using Image @out = NetVips.Image.SvgloadSource(source, out var flags, dpi: double, scale: double, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Flags for this file. + /// Render at this DPI. + /// Scale output by this factor. + /// Allow SVG of any size. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image SvgloadSource(Source source, out Enums.ForeignFlags flags, double? dpi = null, double? scale = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(dpi), dpi); + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(unlimited), unlimited); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("svgload_source", options, source) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load svg from stream. + /// + /// + /// + /// using Image @out = NetVips.Image.SvgloadStream(stream, out var flags, dpi: double, scale: double, unlimited: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Flags for this file. + /// Render at this DPI. + /// Scale output by this factor. + /// Allow SVG of any size. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image SvgloadStream(Stream stream, out Enums.ForeignFlags flags, double? dpi = null, double? scale = null, bool? unlimited = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = SvgloadSource(source, out flags, dpi, scale, unlimited, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Find the index of the first non-zero pixel in tests. + /// + /// + /// + /// using Image @out = NetVips.Image.Switch(tests); + /// + /// + /// Table of images to test. + /// A new . + public static Image Switch(params Image[] tests) + { + return Operation.Call("switch", new object[] { tests }) as Image; + } + + /// + /// Run an external command. + /// + /// + /// + /// NetVips.Image.System(cmdFormat, @in: Image[], outFormat: string, inFormat: string); + /// + /// + /// Command to run. + /// Array of input images. + /// Format for output filename. + /// Format for input filename. + public static void System(string cmdFormat, Image[] @in = null, string outFormat = null, string inFormat = null) + { + var options = new VOption(); + + options.AddIfPresent("in", @in); + options.AddIfPresent("out_format", outFormat); + options.AddIfPresent("in_format", inFormat); + + Operation.Call("system", options, cmdFormat); + } + + /// + /// Run an external command. + /// + /// + /// + /// NetVips.Image.System(cmdFormat, out var @out, @in: Image[], outFormat: string, inFormat: string); + /// + /// + /// Command to run. + /// Output image. + /// Array of input images. + /// Format for output filename. + /// Format for input filename. + public static void System(string cmdFormat, out Image @out, Image[] @in = null, string outFormat = null, string inFormat = null) + { + var options = new VOption(); + + options.AddIfPresent("in", @in); + options.AddIfPresent("out_format", outFormat); + options.AddIfPresent("in_format", inFormat); + + options.Add("out", true); + + var results = Operation.Call("system", options, cmdFormat) as object[]; + + var opts = results?[1] as VOption; + @out = opts?["out"] as Image; + } + + /// + /// Run an external command. + /// + /// + /// + /// NetVips.Image.System(cmdFormat, out var @out, out var log, @in: Image[], outFormat: string, inFormat: string); + /// + /// + /// Command to run. + /// Output image. + /// Command log. + /// Array of input images. + /// Format for output filename. + /// Format for input filename. + public static void System(string cmdFormat, out Image @out, out string log, Image[] @in = null, string outFormat = null, string inFormat = null) + { + var options = new VOption(); + + options.AddIfPresent("in", @in); + options.AddIfPresent("out_format", outFormat); + options.AddIfPresent("in_format", inFormat); + + options.Add("out", true); + options.Add("log", true); + + var results = Operation.Call("system", options, cmdFormat) as object[]; + + var opts = results?[1] as VOption; + @out = opts?["out"] as Image; + log = opts?["log"] is string out2 ? out2 : null; + } + + /// + /// Make a text image. + /// + /// + /// + /// using Image @out = NetVips.Image.Text(text, font: string, width: int, height: int, align: Enums.Align, justify: bool, dpi: int, spacing: int, fontfile: string, rgba: bool, wrap: Enums.TextWrap); + /// + /// + /// Text to render. + /// Font to render with. + /// Maximum image width in pixels. + /// Maximum image height in pixels. + /// Align on the low, centre or high edge. + /// Justify lines. + /// DPI to render at. + /// Line spacing. + /// Load this font file. + /// Enable RGBA output. + /// Wrap lines on word or character boundaries. + /// A new . + public static Image Text(string text, string font = null, int? width = null, int? height = null, Enums.Align? align = null, bool? justify = null, int? dpi = null, int? spacing = null, string fontfile = null, bool? rgba = null, Enums.TextWrap? wrap = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(font), font); + options.AddIfPresent(nameof(width), width); + options.AddIfPresent(nameof(height), height); + options.AddIfPresent(nameof(align), align); + options.AddIfPresent(nameof(justify), justify); + options.AddIfPresent(nameof(dpi), dpi); + options.AddIfPresent(nameof(spacing), spacing); + options.AddIfPresent(nameof(fontfile), fontfile); + options.AddIfPresent(nameof(rgba), rgba); + options.AddIfPresent(nameof(wrap), wrap); + + return Operation.Call("text", options, text) as Image; + } + + /// + /// Make a text image. + /// + /// + /// + /// using Image @out = NetVips.Image.Text(text, out var autofitDpi, font: string, width: int, height: int, align: Enums.Align, justify: bool, dpi: int, spacing: int, fontfile: string, rgba: bool, wrap: Enums.TextWrap); + /// + /// + /// Text to render. + /// DPI selected by autofit. + /// Font to render with. + /// Maximum image width in pixels. + /// Maximum image height in pixels. + /// Align on the low, centre or high edge. + /// Justify lines. + /// DPI to render at. + /// Line spacing. + /// Load this font file. + /// Enable RGBA output. + /// Wrap lines on word or character boundaries. + /// A new . + public static Image Text(string text, out int autofitDpi, string font = null, int? width = null, int? height = null, Enums.Align? align = null, bool? justify = null, int? dpi = null, int? spacing = null, string fontfile = null, bool? rgba = null, Enums.TextWrap? wrap = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(font), font); + options.AddIfPresent(nameof(width), width); + options.AddIfPresent(nameof(height), height); + options.AddIfPresent(nameof(align), align); + options.AddIfPresent(nameof(justify), justify); + options.AddIfPresent(nameof(dpi), dpi); + options.AddIfPresent(nameof(spacing), spacing); + options.AddIfPresent(nameof(fontfile), fontfile); + options.AddIfPresent(nameof(rgba), rgba); + options.AddIfPresent(nameof(wrap), wrap); + + options.Add("autofit_dpi", true); + + var results = Operation.Call("text", options, text) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + autofitDpi = opts?["autofit_dpi"] is int out1 ? out1 : 0; + + return finalResult; + } + + /// + /// Generate thumbnail from file. + /// + /// + /// + /// using Image @out = NetVips.Image.Thumbnail(filename, width, height: int, size: Enums.Size, noRotate: bool, crop: Enums.Interesting, linear: bool, importProfile: string, exportProfile: string, intent: Enums.Intent, failOn: Enums.FailOn); + /// + /// + /// Filename to read from. + /// Size to this width. + /// Size to this height. + /// Only upsize, only downsize, or both. + /// Don't use orientation tags to rotate image upright. + /// Reduce to fill target rectangle, then crop. + /// Reduce in linear light. + /// Fallback import profile. + /// Fallback export profile. + /// Rendering intent. + /// Error level to fail on. + /// A new . + public static Image Thumbnail(string filename, int width, int? height = null, Enums.Size? size = null, bool? noRotate = null, Enums.Interesting? crop = null, bool? linear = null, string importProfile = null, string exportProfile = null, Enums.Intent? intent = null, Enums.FailOn? failOn = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(height), height); + options.AddIfPresent(nameof(size), size); + options.AddIfPresent("no_rotate", noRotate); + options.AddIfPresent(nameof(crop), crop); + options.AddIfPresent(nameof(linear), linear); + options.AddIfPresent("import_profile", importProfile); + options.AddIfPresent("export_profile", exportProfile); + options.AddIfPresent(nameof(intent), intent); + options.AddFailOn(failOn); + + return Operation.Call("thumbnail", options, filename, width) as Image; + } + + /// + /// Generate thumbnail from buffer. + /// + /// + /// + /// using Image @out = NetVips.Image.ThumbnailBuffer(buffer, width, optionString: string, height: int, size: Enums.Size, noRotate: bool, crop: Enums.Interesting, linear: bool, importProfile: string, exportProfile: string, intent: Enums.Intent, failOn: Enums.FailOn); + /// + /// + /// Buffer to load from. + /// Size to this width. + /// Options that are passed on to the underlying loader. + /// Size to this height. + /// Only upsize, only downsize, or both. + /// Don't use orientation tags to rotate image upright. + /// Reduce to fill target rectangle, then crop. + /// Reduce in linear light. + /// Fallback import profile. + /// Fallback export profile. + /// Rendering intent. + /// Error level to fail on. + /// A new . + public static Image ThumbnailBuffer(byte[] buffer, int width, string optionString = null, int? height = null, Enums.Size? size = null, bool? noRotate = null, Enums.Interesting? crop = null, bool? linear = null, string importProfile = null, string exportProfile = null, Enums.Intent? intent = null, Enums.FailOn? failOn = null) + { + var options = new VOption(); + + options.AddIfPresent("option_string", optionString); + options.AddIfPresent(nameof(height), height); + options.AddIfPresent(nameof(size), size); + options.AddIfPresent("no_rotate", noRotate); + options.AddIfPresent(nameof(crop), crop); + options.AddIfPresent(nameof(linear), linear); + options.AddIfPresent("import_profile", importProfile); + options.AddIfPresent("export_profile", exportProfile); + options.AddIfPresent(nameof(intent), intent); + options.AddFailOn(failOn); + + return Operation.Call("thumbnail_buffer", options, buffer, width) as Image; + } + + /// + /// Generate thumbnail from image. + /// + /// + /// + /// using Image @out = in.ThumbnailImage(width, height: int, size: Enums.Size, noRotate: bool, crop: Enums.Interesting, linear: bool, importProfile: string, exportProfile: string, intent: Enums.Intent, failOn: Enums.FailOn); + /// + /// + /// Size to this width. + /// Size to this height. + /// Only upsize, only downsize, or both. + /// Don't use orientation tags to rotate image upright. + /// Reduce to fill target rectangle, then crop. + /// Reduce in linear light. + /// Fallback import profile. + /// Fallback export profile. + /// Rendering intent. + /// Error level to fail on. + /// A new . + public Image ThumbnailImage(int width, int? height = null, Enums.Size? size = null, bool? noRotate = null, Enums.Interesting? crop = null, bool? linear = null, string importProfile = null, string exportProfile = null, Enums.Intent? intent = null, Enums.FailOn? failOn = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(height), height); + options.AddIfPresent(nameof(size), size); + options.AddIfPresent("no_rotate", noRotate); + options.AddIfPresent(nameof(crop), crop); + options.AddIfPresent(nameof(linear), linear); + options.AddIfPresent("import_profile", importProfile); + options.AddIfPresent("export_profile", exportProfile); + options.AddIfPresent(nameof(intent), intent); + options.AddFailOn(failOn); + + return this.Call("thumbnail_image", options, width) as Image; + } + + /// + /// Generate thumbnail from source. + /// + /// + /// + /// using Image @out = NetVips.Image.ThumbnailSource(source, width, optionString: string, height: int, size: Enums.Size, noRotate: bool, crop: Enums.Interesting, linear: bool, importProfile: string, exportProfile: string, intent: Enums.Intent, failOn: Enums.FailOn); + /// + /// + /// Source to load from. + /// Size to this width. + /// Options that are passed on to the underlying loader. + /// Size to this height. + /// Only upsize, only downsize, or both. + /// Don't use orientation tags to rotate image upright. + /// Reduce to fill target rectangle, then crop. + /// Reduce in linear light. + /// Fallback import profile. + /// Fallback export profile. + /// Rendering intent. + /// Error level to fail on. + /// A new . + public static Image ThumbnailSource(Source source, int width, string optionString = null, int? height = null, Enums.Size? size = null, bool? noRotate = null, Enums.Interesting? crop = null, bool? linear = null, string importProfile = null, string exportProfile = null, Enums.Intent? intent = null, Enums.FailOn? failOn = null) + { + var options = new VOption(); + + options.AddIfPresent("option_string", optionString); + options.AddIfPresent(nameof(height), height); + options.AddIfPresent(nameof(size), size); + options.AddIfPresent("no_rotate", noRotate); + options.AddIfPresent(nameof(crop), crop); + options.AddIfPresent(nameof(linear), linear); + options.AddIfPresent("import_profile", importProfile); + options.AddIfPresent("export_profile", exportProfile); + options.AddIfPresent(nameof(intent), intent); + options.AddFailOn(failOn); + + return Operation.Call("thumbnail_source", options, source, width) as Image; + } + + /// + /// Generate thumbnail from stream. + /// + /// + /// + /// using Image @out = NetVips.Image.ThumbnailStream(stream, width, optionString: string, height: int, size: Enums.Size, noRotate: bool, crop: Enums.Interesting, linear: bool, importProfile: string, exportProfile: string, intent: Enums.Intent, failOn: Enums.FailOn); + /// + /// + /// Stream to load from. + /// Size to this width. + /// Options that are passed on to the underlying loader. + /// Size to this height. + /// Only upsize, only downsize, or both. + /// Don't use orientation tags to rotate image upright. + /// Reduce to fill target rectangle, then crop. + /// Reduce in linear light. + /// Fallback import profile. + /// Fallback export profile. + /// Rendering intent. + /// Error level to fail on. + /// A new . + public static Image ThumbnailStream(Stream stream, int width, string optionString = null, int? height = null, Enums.Size? size = null, bool? noRotate = null, Enums.Interesting? crop = null, bool? linear = null, string importProfile = null, string exportProfile = null, Enums.Intent? intent = null, Enums.FailOn? failOn = null) + { + var source = SourceStream.NewFromStream(stream); + var image = ThumbnailSource(source, width, optionString, height, size, noRotate, crop, linear, importProfile, exportProfile, intent, failOn); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load tiff from file. + /// + /// + /// + /// using Image @out = NetVips.Image.Tiffload(filename, page: int, subifd: int, n: int, autorotate: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// First page to load. + /// Subifd index. + /// Number of pages to load, -1 for all. + /// Rotate image using orientation tag. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Tiffload(string filename, int? page = null, int? subifd = null, int? n = null, bool? autorotate = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(subifd), subifd); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(autorotate), autorotate); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("tiffload", options, filename) as Image; + } + + /// + /// Load tiff from file. + /// + /// + /// + /// using Image @out = NetVips.Image.Tiffload(filename, out var flags, page: int, subifd: int, n: int, autorotate: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// First page to load. + /// Subifd index. + /// Number of pages to load, -1 for all. + /// Rotate image using orientation tag. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Tiffload(string filename, out Enums.ForeignFlags flags, int? page = null, int? subifd = null, int? n = null, bool? autorotate = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(subifd), subifd); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(autorotate), autorotate); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("tiffload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load tiff from buffer. + /// + /// + /// + /// using Image @out = NetVips.Image.TiffloadBuffer(buffer, page: int, subifd: int, n: int, autorotate: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// First page to load. + /// Subifd index. + /// Number of pages to load, -1 for all. + /// Rotate image using orientation tag. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image TiffloadBuffer(byte[] buffer, int? page = null, int? subifd = null, int? n = null, bool? autorotate = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(subifd), subifd); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(autorotate), autorotate); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("tiffload_buffer", options, buffer) as Image; + } + + /// + /// Load tiff from buffer. + /// + /// + /// + /// using Image @out = NetVips.Image.TiffloadBuffer(buffer, out var flags, page: int, subifd: int, n: int, autorotate: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Flags for this file. + /// First page to load. + /// Subifd index. + /// Number of pages to load, -1 for all. + /// Rotate image using orientation tag. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image TiffloadBuffer(byte[] buffer, out Enums.ForeignFlags flags, int? page = null, int? subifd = null, int? n = null, bool? autorotate = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(subifd), subifd); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(autorotate), autorotate); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("tiffload_buffer", options, buffer) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load tiff from source. + /// + /// + /// + /// using Image @out = NetVips.Image.TiffloadSource(source, page: int, subifd: int, n: int, autorotate: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// First page to load. + /// Subifd index. + /// Number of pages to load, -1 for all. + /// Rotate image using orientation tag. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image TiffloadSource(Source source, int? page = null, int? subifd = null, int? n = null, bool? autorotate = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(subifd), subifd); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(autorotate), autorotate); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("tiffload_source", options, source) as Image; + } + + /// + /// Load tiff from stream. + /// + /// + /// + /// using Image @out = NetVips.Image.TiffloadStream(stream, page: int, subifd: int, n: int, autorotate: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// First page to load. + /// Subifd index. + /// Number of pages to load, -1 for all. + /// Rotate image using orientation tag. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image TiffloadStream(Stream stream, int? page = null, int? subifd = null, int? n = null, bool? autorotate = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = TiffloadSource(source, page, subifd, n, autorotate, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load tiff from source. + /// + /// + /// + /// using Image @out = NetVips.Image.TiffloadSource(source, out var flags, page: int, subifd: int, n: int, autorotate: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Flags for this file. + /// First page to load. + /// Subifd index. + /// Number of pages to load, -1 for all. + /// Rotate image using orientation tag. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image TiffloadSource(Source source, out Enums.ForeignFlags flags, int? page = null, int? subifd = null, int? n = null, bool? autorotate = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(subifd), subifd); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(autorotate), autorotate); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("tiffload_source", options, source) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load tiff from stream. + /// + /// + /// + /// using Image @out = NetVips.Image.TiffloadStream(stream, out var flags, page: int, subifd: int, n: int, autorotate: bool, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Flags for this file. + /// First page to load. + /// Subifd index. + /// Number of pages to load, -1 for all. + /// Rotate image using orientation tag. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image TiffloadStream(Stream stream, out Enums.ForeignFlags flags, int? page = null, int? subifd = null, int? n = null, bool? autorotate = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = TiffloadSource(source, out flags, page, subifd, n, autorotate, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Save image to tiff file. + /// + /// + /// + /// in.Tiffsave(filename, compression: Enums.ForeignTiffCompression, q: int, predictor: Enums.ForeignTiffPredictor, tile: bool, tileWidth: int, tileHeight: int, pyramid: bool, miniswhite: bool, bitdepth: int, resunit: Enums.ForeignTiffResunit, xres: double, yres: double, bigtiff: bool, properties: bool, regionShrink: Enums.RegionShrink, level: int, lossless: bool, depth: Enums.ForeignDzDepth, subifd: bool, premultiply: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Filename to save to. + /// Compression for this file. + /// Q factor. + /// Compression prediction. + /// Write a tiled tiff. + /// Tile width in pixels. + /// Tile height in pixels. + /// Write a pyramidal tiff. + /// Use 0 for white in 1-bit images. + /// Write as a 1, 2, 4 or 8 bit image. + /// Resolution unit. + /// Horizontal resolution in pixels/mm. + /// Vertical resolution in pixels/mm. + /// Write a bigtiff image. + /// Write a properties document to IMAGEDESCRIPTION. + /// Method to shrink regions. + /// ZSTD compression level. + /// Enable WEBP lossless mode. + /// Pyramid depth. + /// Save pyr layers as sub-IFDs. + /// Save with premultiplied alpha. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Tiffsave(string filename, Enums.ForeignTiffCompression? compression = null, int? q = null, Enums.ForeignTiffPredictor? predictor = null, bool? tile = null, int? tileWidth = null, int? tileHeight = null, bool? pyramid = null, bool? miniswhite = null, int? bitdepth = null, Enums.ForeignTiffResunit? resunit = null, double? xres = null, double? yres = null, bool? bigtiff = null, bool? properties = null, Enums.RegionShrink? regionShrink = null, int? level = null, bool? lossless = null, Enums.ForeignDzDepth? depth = null, bool? subifd = null, bool? premultiply = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(compression), compression); + options.AddIfPresent("Q", q); + options.AddIfPresent(nameof(predictor), predictor); + options.AddIfPresent(nameof(tile), tile); + options.AddIfPresent("tile_width", tileWidth); + options.AddIfPresent("tile_height", tileHeight); + options.AddIfPresent(nameof(pyramid), pyramid); + options.AddIfPresent(nameof(miniswhite), miniswhite); + options.AddIfPresent(nameof(bitdepth), bitdepth); + options.AddIfPresent(nameof(resunit), resunit); + options.AddIfPresent(nameof(xres), xres); + options.AddIfPresent(nameof(yres), yres); + options.AddIfPresent(nameof(bigtiff), bigtiff); + options.AddIfPresent(nameof(properties), properties); + options.AddIfPresent("region_shrink", regionShrink); + options.AddIfPresent(nameof(level), level); + options.AddIfPresent(nameof(lossless), lossless); + options.AddIfPresent(nameof(depth), depth); + options.AddIfPresent(nameof(subifd), subifd); + options.AddIfPresent(nameof(premultiply), premultiply); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("tiffsave", options, filename); + } + + /// + /// Save image to tiff buffer. + /// + /// + /// + /// byte[] buffer = in.TiffsaveBuffer(compression: Enums.ForeignTiffCompression, q: int, predictor: Enums.ForeignTiffPredictor, tile: bool, tileWidth: int, tileHeight: int, pyramid: bool, miniswhite: bool, bitdepth: int, resunit: Enums.ForeignTiffResunit, xres: double, yres: double, bigtiff: bool, properties: bool, regionShrink: Enums.RegionShrink, level: int, lossless: bool, depth: Enums.ForeignDzDepth, subifd: bool, premultiply: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Compression for this file. + /// Q factor. + /// Compression prediction. + /// Write a tiled tiff. + /// Tile width in pixels. + /// Tile height in pixels. + /// Write a pyramidal tiff. + /// Use 0 for white in 1-bit images. + /// Write as a 1, 2, 4 or 8 bit image. + /// Resolution unit. + /// Horizontal resolution in pixels/mm. + /// Vertical resolution in pixels/mm. + /// Write a bigtiff image. + /// Write a properties document to IMAGEDESCRIPTION. + /// Method to shrink regions. + /// ZSTD compression level. + /// Enable WEBP lossless mode. + /// Pyramid depth. + /// Save pyr layers as sub-IFDs. + /// Save with premultiplied alpha. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + /// An array of bytes. + public byte[] TiffsaveBuffer(Enums.ForeignTiffCompression? compression = null, int? q = null, Enums.ForeignTiffPredictor? predictor = null, bool? tile = null, int? tileWidth = null, int? tileHeight = null, bool? pyramid = null, bool? miniswhite = null, int? bitdepth = null, Enums.ForeignTiffResunit? resunit = null, double? xres = null, double? yres = null, bool? bigtiff = null, bool? properties = null, Enums.RegionShrink? regionShrink = null, int? level = null, bool? lossless = null, Enums.ForeignDzDepth? depth = null, bool? subifd = null, bool? premultiply = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(compression), compression); + options.AddIfPresent("Q", q); + options.AddIfPresent(nameof(predictor), predictor); + options.AddIfPresent(nameof(tile), tile); + options.AddIfPresent("tile_width", tileWidth); + options.AddIfPresent("tile_height", tileHeight); + options.AddIfPresent(nameof(pyramid), pyramid); + options.AddIfPresent(nameof(miniswhite), miniswhite); + options.AddIfPresent(nameof(bitdepth), bitdepth); + options.AddIfPresent(nameof(resunit), resunit); + options.AddIfPresent(nameof(xres), xres); + options.AddIfPresent(nameof(yres), yres); + options.AddIfPresent(nameof(bigtiff), bigtiff); + options.AddIfPresent(nameof(properties), properties); + options.AddIfPresent("region_shrink", regionShrink); + options.AddIfPresent(nameof(level), level); + options.AddIfPresent(nameof(lossless), lossless); + options.AddIfPresent(nameof(depth), depth); + options.AddIfPresent(nameof(subifd), subifd); + options.AddIfPresent(nameof(premultiply), premultiply); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + return this.Call("tiffsave_buffer", options) as byte[]; + } + + /// + /// Save image to tiff target. + /// + /// + /// + /// in.TiffsaveTarget(target, compression: Enums.ForeignTiffCompression, q: int, predictor: Enums.ForeignTiffPredictor, tile: bool, tileWidth: int, tileHeight: int, pyramid: bool, miniswhite: bool, bitdepth: int, resunit: Enums.ForeignTiffResunit, xres: double, yres: double, bigtiff: bool, properties: bool, regionShrink: Enums.RegionShrink, level: int, lossless: bool, depth: Enums.ForeignDzDepth, subifd: bool, premultiply: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Target to save to. + /// Compression for this file. + /// Q factor. + /// Compression prediction. + /// Write a tiled tiff. + /// Tile width in pixels. + /// Tile height in pixels. + /// Write a pyramidal tiff. + /// Use 0 for white in 1-bit images. + /// Write as a 1, 2, 4 or 8 bit image. + /// Resolution unit. + /// Horizontal resolution in pixels/mm. + /// Vertical resolution in pixels/mm. + /// Write a bigtiff image. + /// Write a properties document to IMAGEDESCRIPTION. + /// Method to shrink regions. + /// ZSTD compression level. + /// Enable WEBP lossless mode. + /// Pyramid depth. + /// Save pyr layers as sub-IFDs. + /// Save with premultiplied alpha. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void TiffsaveTarget(Target target, Enums.ForeignTiffCompression? compression = null, int? q = null, Enums.ForeignTiffPredictor? predictor = null, bool? tile = null, int? tileWidth = null, int? tileHeight = null, bool? pyramid = null, bool? miniswhite = null, int? bitdepth = null, Enums.ForeignTiffResunit? resunit = null, double? xres = null, double? yres = null, bool? bigtiff = null, bool? properties = null, Enums.RegionShrink? regionShrink = null, int? level = null, bool? lossless = null, Enums.ForeignDzDepth? depth = null, bool? subifd = null, bool? premultiply = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(compression), compression); + options.AddIfPresent("Q", q); + options.AddIfPresent(nameof(predictor), predictor); + options.AddIfPresent(nameof(tile), tile); + options.AddIfPresent("tile_width", tileWidth); + options.AddIfPresent("tile_height", tileHeight); + options.AddIfPresent(nameof(pyramid), pyramid); + options.AddIfPresent(nameof(miniswhite), miniswhite); + options.AddIfPresent(nameof(bitdepth), bitdepth); + options.AddIfPresent(nameof(resunit), resunit); + options.AddIfPresent(nameof(xres), xres); + options.AddIfPresent(nameof(yres), yres); + options.AddIfPresent(nameof(bigtiff), bigtiff); + options.AddIfPresent(nameof(properties), properties); + options.AddIfPresent("region_shrink", regionShrink); + options.AddIfPresent(nameof(level), level); + options.AddIfPresent(nameof(lossless), lossless); + options.AddIfPresent(nameof(depth), depth); + options.AddIfPresent(nameof(subifd), subifd); + options.AddIfPresent(nameof(premultiply), premultiply); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("tiffsave_target", options, target); + } + + /// + /// Save image to tiff stream. + /// + /// + /// + /// in.TiffsaveStream(stream, compression: Enums.ForeignTiffCompression, q: int, predictor: Enums.ForeignTiffPredictor, tile: bool, tileWidth: int, tileHeight: int, pyramid: bool, miniswhite: bool, bitdepth: int, resunit: Enums.ForeignTiffResunit, xres: double, yres: double, bigtiff: bool, properties: bool, regionShrink: Enums.RegionShrink, level: int, lossless: bool, depth: Enums.ForeignDzDepth, subifd: bool, premultiply: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Stream to save to. + /// Compression for this file. + /// Q factor. + /// Compression prediction. + /// Write a tiled tiff. + /// Tile width in pixels. + /// Tile height in pixels. + /// Write a pyramidal tiff. + /// Use 0 for white in 1-bit images. + /// Write as a 1, 2, 4 or 8 bit image. + /// Resolution unit. + /// Horizontal resolution in pixels/mm. + /// Vertical resolution in pixels/mm. + /// Write a bigtiff image. + /// Write a properties document to IMAGEDESCRIPTION. + /// Method to shrink regions. + /// ZSTD compression level. + /// Enable WEBP lossless mode. + /// Pyramid depth. + /// Save pyr layers as sub-IFDs. + /// Save with premultiplied alpha. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void TiffsaveStream(Stream stream, Enums.ForeignTiffCompression? compression = null, int? q = null, Enums.ForeignTiffPredictor? predictor = null, bool? tile = null, int? tileWidth = null, int? tileHeight = null, bool? pyramid = null, bool? miniswhite = null, int? bitdepth = null, Enums.ForeignTiffResunit? resunit = null, double? xres = null, double? yres = null, bool? bigtiff = null, bool? properties = null, Enums.RegionShrink? regionShrink = null, int? level = null, bool? lossless = null, Enums.ForeignDzDepth? depth = null, bool? subifd = null, bool? premultiply = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + using var target = TargetStream.NewFromStream(stream); + TiffsaveTarget(target, compression, q, predictor, tile, tileWidth, tileHeight, pyramid, miniswhite, bitdepth, resunit, xres, yres, bigtiff, properties, regionShrink, level, lossless, depth, subifd, premultiply, keep, background, pageHeight, profile); + } + + /// + /// Cache an image as a set of tiles. + /// + /// + /// + /// using Image @out = in.Tilecache(tileWidth: int, tileHeight: int, maxTiles: int, access: Enums.Access, threaded: bool, persistent: bool); + /// + /// + /// Tile width in pixels. + /// Tile height in pixels. + /// Maximum number of tiles to cache. + /// Expected access pattern. + /// Allow threaded access. + /// Keep cache between evaluations. + /// A new . + public Image Tilecache(int? tileWidth = null, int? tileHeight = null, int? maxTiles = null, Enums.Access? access = null, bool? threaded = null, bool? persistent = null) + { + var options = new VOption(); + + options.AddIfPresent("tile_width", tileWidth); + options.AddIfPresent("tile_height", tileHeight); + options.AddIfPresent("max_tiles", maxTiles); + options.AddIfPresent(nameof(access), access); + options.AddIfPresent(nameof(threaded), threaded); + options.AddIfPresent(nameof(persistent), persistent); + + return this.Call("tilecache", options) as Image; + } + + /// + /// Build a look-up table. + /// + /// + /// + /// using Image @out = NetVips.Image.Tonelut(inMax: int, outMax: int, lb: double, lw: double, ps: double, pm: double, ph: double, s: double, m: double, h: double); + /// + /// + /// Size of LUT to build. + /// Maximum value in output LUT. + /// Lowest value in output. + /// Highest value in output. + /// Position of shadow. + /// Position of mid-tones. + /// Position of highlights. + /// Adjust shadows by this much. + /// Adjust mid-tones by this much. + /// Adjust highlights by this much. + /// A new . + public static Image Tonelut(int? inMax = null, int? outMax = null, double? lb = null, double? lw = null, double? ps = null, double? pm = null, double? ph = null, double? s = null, double? m = null, double? h = null) + { + var options = new VOption(); + + options.AddIfPresent("in_max", inMax); + options.AddIfPresent("out_max", outMax); + options.AddIfPresent("Lb", lb); + options.AddIfPresent("Lw", lw); + options.AddIfPresent("Ps", ps); + options.AddIfPresent("Pm", pm); + options.AddIfPresent("Ph", ph); + options.AddIfPresent("S", s); + options.AddIfPresent("M", m); + options.AddIfPresent("H", h); + + return Operation.Call("tonelut", options) as Image; + } + + /// + /// Transpose3d an image. + /// + /// + /// + /// using Image @out = in.Transpose3d(pageHeight: int); + /// + /// + /// Height of each input page. + /// A new . + public Image Transpose3d(int? pageHeight = null) + { + var options = new VOption(); + + options.AddIfPresent("page_height", pageHeight); + + return this.Call("transpose3d", options) as Image; + } + + /// + /// Unpremultiply image alpha. + /// + /// + /// + /// using Image @out = in.Unpremultiply(maxAlpha: double, alphaBand: int); + /// + /// + /// Maximum value of alpha channel. + /// Unpremultiply with this alpha. + /// A new . + public Image Unpremultiply(double? maxAlpha = null, int? alphaBand = null) + { + var options = new VOption(); + + options.AddIfPresent("max_alpha", maxAlpha); + options.AddIfPresent("alpha_band", alphaBand); + + return this.Call("unpremultiply", options) as Image; + } + + /// + /// Load vips from file. + /// + /// + /// + /// using Image @out = NetVips.Image.Vipsload(filename, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Vipsload(string filename, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("vipsload", options, filename) as Image; + } + + /// + /// Load vips from file. + /// + /// + /// + /// using Image @out = NetVips.Image.Vipsload(filename, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Vipsload(string filename, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("vipsload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load vips from source. + /// + /// + /// + /// using Image @out = NetVips.Image.VipsloadSource(source, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image VipsloadSource(Source source, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("vipsload_source", options, source) as Image; + } + + /// + /// Load vips from stream. + /// + /// + /// + /// using Image @out = NetVips.Image.VipsloadStream(stream, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image VipsloadStream(Stream stream, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = VipsloadSource(source, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load vips from source. + /// + /// + /// + /// using Image @out = NetVips.Image.VipsloadSource(source, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image VipsloadSource(Source source, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("vipsload_source", options, source) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load vips from stream. + /// + /// + /// + /// using Image @out = NetVips.Image.VipsloadStream(stream, out var flags, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Flags for this file. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image VipsloadStream(Stream stream, out Enums.ForeignFlags flags, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = VipsloadSource(source, out flags, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Save image to file in vips format. + /// + /// + /// + /// in.Vipssave(filename, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Filename to save to. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Vipssave(string filename, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("vipssave", options, filename); + } + + /// + /// Save image to target in vips format. + /// + /// + /// + /// in.VipssaveTarget(target, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Target to save to. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void VipssaveTarget(Target target, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("vipssave_target", options, target); + } + + /// + /// Save image to stream in vips format. + /// + /// + /// + /// in.VipssaveStream(stream, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Stream to save to. + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void VipssaveStream(Stream stream, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + using var target = TargetStream.NewFromStream(stream); + VipssaveTarget(target, keep, background, pageHeight, profile); + } + + /// + /// Load webp from file. + /// + /// + /// + /// using Image @out = NetVips.Image.Webpload(filename, page: int, n: int, scale: double, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Factor to scale by. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Webpload(string filename, int? page = null, int? n = null, double? scale = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("webpload", options, filename) as Image; + } + + /// + /// Load webp from file. + /// + /// + /// + /// using Image @out = NetVips.Image.Webpload(filename, out var flags, page: int, n: int, scale: double, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Filename to load from. + /// Flags for this file. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Factor to scale by. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image Webpload(string filename, out Enums.ForeignFlags flags, int? page = null, int? n = null, double? scale = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("webpload", options, filename) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load webp from buffer. + /// + /// + /// + /// using Image @out = NetVips.Image.WebploadBuffer(buffer, page: int, n: int, scale: double, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Factor to scale by. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image WebploadBuffer(byte[] buffer, int? page = null, int? n = null, double? scale = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("webpload_buffer", options, buffer) as Image; + } + + /// + /// Load webp from buffer. + /// + /// + /// + /// using Image @out = NetVips.Image.WebploadBuffer(buffer, out var flags, page: int, n: int, scale: double, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Buffer to load from. + /// Flags for this file. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Factor to scale by. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image WebploadBuffer(byte[] buffer, out Enums.ForeignFlags flags, int? page = null, int? n = null, double? scale = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("webpload_buffer", options, buffer) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load webp from source. + /// + /// + /// + /// using Image @out = NetVips.Image.WebploadSource(source, page: int, n: int, scale: double, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Factor to scale by. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image WebploadSource(Source source, int? page = null, int? n = null, double? scale = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + return Operation.Call("webpload_source", options, source) as Image; + } + + /// + /// Load webp from stream. + /// + /// + /// + /// using Image @out = NetVips.Image.WebploadStream(stream, page: int, n: int, scale: double, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Factor to scale by. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image WebploadStream(Stream stream, int? page = null, int? n = null, double? scale = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = WebploadSource(source, page, n, scale, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Load webp from source. + /// + /// + /// + /// using Image @out = NetVips.Image.WebploadSource(source, out var flags, page: int, n: int, scale: double, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Source to load from. + /// Flags for this file. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Factor to scale by. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image WebploadSource(Source source, out Enums.ForeignFlags flags, int? page = null, int? n = null, double? scale = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(page), page); + options.AddIfPresent(nameof(n), n); + options.AddIfPresent(nameof(scale), scale); + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + options.AddIfPresent(nameof(revalidate), revalidate); + + options.Add("flags", true); + + var results = Operation.Call("webpload_source", options, source) as object[]; + var finalResult = results?[0] as Image; + var opts = results?[1] as VOption; + flags = (Enums.ForeignFlags)opts?["flags"]; + + return finalResult; + } + + /// + /// Load webp from stream. + /// + /// + /// + /// using Image @out = NetVips.Image.WebploadStream(stream, out var flags, page: int, n: int, scale: double, memory: bool, access: Enums.Access, failOn: Enums.FailOn, revalidate: bool); + /// + /// + /// Stream to load from. + /// Flags for this file. + /// First page to load. + /// Number of pages to load, -1 for all. + /// Factor to scale by. + /// Force open via memory. + /// Required access pattern for this file. + /// Error level to fail on. + /// Don't use a cached result for this operation. + /// A new . + public static Image WebploadStream(Stream stream, out Enums.ForeignFlags flags, int? page = null, int? n = null, double? scale = null, bool? memory = null, Enums.Access? access = null, Enums.FailOn? failOn = null, bool? revalidate = null) + { + var source = SourceStream.NewFromStream(stream); + var image = WebploadSource(source, out flags, page, n, scale, memory, access, failOn, revalidate); + + image.OnPostClose += () => source.Dispose(); + + return image; + } + + /// + /// Save as WebP. + /// + /// + /// + /// in.Webpsave(filename, q: int, lossless: bool, preset: Enums.ForeignWebpPreset, smartSubsample: bool, nearLossless: bool, alphaQ: int, minSize: bool, kmin: int, kmax: int, effort: int, mixed: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Filename to save to. + /// Q factor. + /// Enable lossless compression. + /// Preset for lossy compression. + /// Enable high quality chroma subsampling. + /// Enable preprocessing in lossless mode (uses Q). + /// Change alpha plane fidelity for lossy compression. + /// Optimise for minimum size. + /// Minimum number of frames between key frames. + /// Maximum number of frames between key frames. + /// Level of CPU effort to reduce file size. + /// Allow mixed encoding (might reduce file size). + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void Webpsave(string filename, int? q = null, bool? lossless = null, Enums.ForeignWebpPreset? preset = null, bool? smartSubsample = null, bool? nearLossless = null, int? alphaQ = null, bool? minSize = null, int? kmin = null, int? kmax = null, int? effort = null, bool? mixed = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent("Q", q); + options.AddIfPresent(nameof(lossless), lossless); + options.AddIfPresent(nameof(preset), preset); + options.AddIfPresent("smart_subsample", smartSubsample); + options.AddIfPresent("near_lossless", nearLossless); + options.AddIfPresent("alpha_q", alphaQ); + options.AddIfPresent("min_size", minSize); + options.AddIfPresent(nameof(kmin), kmin); + options.AddIfPresent(nameof(kmax), kmax); + options.AddIfPresent(nameof(effort), effort); + options.AddIfPresent(nameof(mixed), mixed); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("webpsave", options, filename); + } + + /// + /// Save as WebP. + /// + /// + /// + /// byte[] buffer = in.WebpsaveBuffer(q: int, lossless: bool, preset: Enums.ForeignWebpPreset, smartSubsample: bool, nearLossless: bool, alphaQ: int, minSize: bool, kmin: int, kmax: int, effort: int, mixed: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Q factor. + /// Enable lossless compression. + /// Preset for lossy compression. + /// Enable high quality chroma subsampling. + /// Enable preprocessing in lossless mode (uses Q). + /// Change alpha plane fidelity for lossy compression. + /// Optimise for minimum size. + /// Minimum number of frames between key frames. + /// Maximum number of frames between key frames. + /// Level of CPU effort to reduce file size. + /// Allow mixed encoding (might reduce file size). + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + /// An array of bytes. + public byte[] WebpsaveBuffer(int? q = null, bool? lossless = null, Enums.ForeignWebpPreset? preset = null, bool? smartSubsample = null, bool? nearLossless = null, int? alphaQ = null, bool? minSize = null, int? kmin = null, int? kmax = null, int? effort = null, bool? mixed = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent("Q", q); + options.AddIfPresent(nameof(lossless), lossless); + options.AddIfPresent(nameof(preset), preset); + options.AddIfPresent("smart_subsample", smartSubsample); + options.AddIfPresent("near_lossless", nearLossless); + options.AddIfPresent("alpha_q", alphaQ); + options.AddIfPresent("min_size", minSize); + options.AddIfPresent(nameof(kmin), kmin); + options.AddIfPresent(nameof(kmax), kmax); + options.AddIfPresent(nameof(effort), effort); + options.AddIfPresent(nameof(mixed), mixed); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + return this.Call("webpsave_buffer", options) as byte[]; + } + + /// + /// Save image to webp mime. + /// + /// + /// + /// in.WebpsaveMime(q: int, lossless: bool, preset: Enums.ForeignWebpPreset, smartSubsample: bool, nearLossless: bool, alphaQ: int, minSize: bool, kmin: int, kmax: int, effort: int, mixed: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Q factor. + /// Enable lossless compression. + /// Preset for lossy compression. + /// Enable high quality chroma subsampling. + /// Enable preprocessing in lossless mode (uses Q). + /// Change alpha plane fidelity for lossy compression. + /// Optimise for minimum size. + /// Minimum number of frames between key frames. + /// Maximum number of frames between key frames. + /// Level of CPU effort to reduce file size. + /// Allow mixed encoding (might reduce file size). + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void WebpsaveMime(int? q = null, bool? lossless = null, Enums.ForeignWebpPreset? preset = null, bool? smartSubsample = null, bool? nearLossless = null, int? alphaQ = null, bool? minSize = null, int? kmin = null, int? kmax = null, int? effort = null, bool? mixed = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent("Q", q); + options.AddIfPresent(nameof(lossless), lossless); + options.AddIfPresent(nameof(preset), preset); + options.AddIfPresent("smart_subsample", smartSubsample); + options.AddIfPresent("near_lossless", nearLossless); + options.AddIfPresent("alpha_q", alphaQ); + options.AddIfPresent("min_size", minSize); + options.AddIfPresent(nameof(kmin), kmin); + options.AddIfPresent(nameof(kmax), kmax); + options.AddIfPresent(nameof(effort), effort); + options.AddIfPresent(nameof(mixed), mixed); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("webpsave_mime", options); + } + + /// + /// Save as WebP. + /// + /// + /// + /// in.WebpsaveTarget(target, q: int, lossless: bool, preset: Enums.ForeignWebpPreset, smartSubsample: bool, nearLossless: bool, alphaQ: int, minSize: bool, kmin: int, kmax: int, effort: int, mixed: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Target to save to. + /// Q factor. + /// Enable lossless compression. + /// Preset for lossy compression. + /// Enable high quality chroma subsampling. + /// Enable preprocessing in lossless mode (uses Q). + /// Change alpha plane fidelity for lossy compression. + /// Optimise for minimum size. + /// Minimum number of frames between key frames. + /// Maximum number of frames between key frames. + /// Level of CPU effort to reduce file size. + /// Allow mixed encoding (might reduce file size). + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void WebpsaveTarget(Target target, int? q = null, bool? lossless = null, Enums.ForeignWebpPreset? preset = null, bool? smartSubsample = null, bool? nearLossless = null, int? alphaQ = null, bool? minSize = null, int? kmin = null, int? kmax = null, int? effort = null, bool? mixed = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + var options = new VOption(); + + options.AddIfPresent("Q", q); + options.AddIfPresent(nameof(lossless), lossless); + options.AddIfPresent(nameof(preset), preset); + options.AddIfPresent("smart_subsample", smartSubsample); + options.AddIfPresent("near_lossless", nearLossless); + options.AddIfPresent("alpha_q", alphaQ); + options.AddIfPresent("min_size", minSize); + options.AddIfPresent(nameof(kmin), kmin); + options.AddIfPresent(nameof(kmax), kmax); + options.AddIfPresent(nameof(effort), effort); + options.AddIfPresent(nameof(mixed), mixed); + options.AddForeignKeep(keep); + options.AddIfPresent(nameof(background), background); + options.AddIfPresent("page_height", pageHeight); + options.AddIfPresent(nameof(profile), profile); + + this.Call("webpsave_target", options, target); + } + + /// + /// Save as WebP. + /// + /// + /// + /// in.WebpsaveStream(stream, q: int, lossless: bool, preset: Enums.ForeignWebpPreset, smartSubsample: bool, nearLossless: bool, alphaQ: int, minSize: bool, kmin: int, kmax: int, effort: int, mixed: bool, keep: Enums.ForeignKeep, background: double[], pageHeight: int, profile: string); + /// + /// + /// Stream to save to. + /// Q factor. + /// Enable lossless compression. + /// Preset for lossy compression. + /// Enable high quality chroma subsampling. + /// Enable preprocessing in lossless mode (uses Q). + /// Change alpha plane fidelity for lossy compression. + /// Optimise for minimum size. + /// Minimum number of frames between key frames. + /// Maximum number of frames between key frames. + /// Level of CPU effort to reduce file size. + /// Allow mixed encoding (might reduce file size). + /// Which metadata to retain. + /// Background value. + /// Set page height for multipage save. + /// Filename of ICC profile to embed. + public void WebpsaveStream(Stream stream, int? q = null, bool? lossless = null, Enums.ForeignWebpPreset? preset = null, bool? smartSubsample = null, bool? nearLossless = null, int? alphaQ = null, bool? minSize = null, int? kmin = null, int? kmax = null, int? effort = null, bool? mixed = null, Enums.ForeignKeep? keep = null, double[] background = null, int? pageHeight = null, string profile = null) + { + using var target = TargetStream.NewFromStream(stream); + WebpsaveTarget(target, q, lossless, preset, smartSubsample, nearLossless, alphaQ, minSize, kmin, kmax, effort, mixed, keep, background, pageHeight, profile); + } + + /// + /// Make a worley noise image. + /// + /// + /// + /// using Image @out = NetVips.Image.Worley(width, height, cellSize: int, seed: int); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Size of Worley cells. + /// Random number seed. + /// A new . + public static Image Worley(int width, int height, int? cellSize = null, int? seed = null) + { + var options = new VOption(); + + options.AddIfPresent("cell_size", cellSize); + options.AddIfPresent(nameof(seed), seed); + + return Operation.Call("worley", options, width, height) as Image; + } + + /// + /// Wrap image origin. + /// + /// + /// + /// using Image @out = in.Wrap(x: int, y: int); + /// + /// + /// Left edge of input in output. + /// Top edge of input in output. + /// A new . + public Image Wrap(int? x = null, int? y = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(x), x); + options.AddIfPresent(nameof(y), y); + + return this.Call("wrap", options) as Image; + } + + /// + /// Make an image where pixel values are coordinates. + /// + /// + /// + /// using Image @out = NetVips.Image.Xyz(width, height, csize: int, dsize: int, esize: int); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Size of third dimension. + /// Size of fourth dimension. + /// Size of fifth dimension. + /// A new . + public static Image Xyz(int width, int height, int? csize = null, int? dsize = null, int? esize = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(csize), csize); + options.AddIfPresent(nameof(dsize), dsize); + options.AddIfPresent(nameof(esize), esize); + + return Operation.Call("xyz", options, width, height) as Image; + } + + /// + /// Transform XYZ to CMYK. + /// + /// + /// + /// using Image @out = in.XYZ2CMYK(); + /// + /// + /// A new . + public Image XYZ2CMYK() + { + return this.Call("XYZ2CMYK") as Image; + } + + /// + /// Transform XYZ to Lab. + /// + /// + /// + /// using Image @out = in.XYZ2Lab(temp: double[]); + /// + /// + /// Colour temperature. + /// A new . + public Image XYZ2Lab(double[] temp = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(temp), temp); + + return this.Call("XYZ2Lab", options) as Image; + } + + /// + /// Transform XYZ to scRGB. + /// + /// + /// + /// using Image @out = in.XYZ2scRGB(); + /// + /// + /// A new . + public Image XYZ2scRGB() + { + return this.Call("XYZ2scRGB") as Image; + } + + /// + /// Transform XYZ to Yxy. + /// + /// + /// + /// using Image @out = in.XYZ2Yxy(); + /// + /// + /// A new . + public Image XYZ2Yxy() + { + return this.Call("XYZ2Yxy") as Image; + } + + /// + /// Transform Yxy to XYZ. + /// + /// + /// + /// using Image @out = in.Yxy2XYZ(); + /// + /// + /// A new . + public Image Yxy2XYZ() + { + return this.Call("Yxy2XYZ") as Image; + } + + /// + /// Make a zone plate. + /// + /// + /// + /// using Image @out = NetVips.Image.Zone(width, height, uchar: bool); + /// + /// + /// Image width in pixels. + /// Image height in pixels. + /// Output an unsigned char image. + /// A new . + public static Image Zone(int width, int height, bool? uchar = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(uchar), uchar); + + return Operation.Call("zone", options, width, height) as Image; + } + + /// + /// Zoom an image. + /// + /// + /// + /// using Image @out = input.Zoom(xfac, yfac); + /// + /// + /// Horizontal zoom factor. + /// Vertical zoom factor. + /// A new . + public Image Zoom(int xfac, int yfac) + { + return this.Call("zoom", xfac, yfac) as Image; + } + + #endregion + + #region auto-generated properties + + /// + /// Image width in pixels + /// + public int Width => (int)Get("width"); + + /// + /// Image height in pixels + /// + public int Height => (int)Get("height"); + + /// + /// Number of bands in image + /// + public int Bands => (int)Get("bands"); + + /// + /// Pixel format in image + /// + public Enums.BandFormat Format => (Enums.BandFormat)Get("format"); + + /// + /// Pixel coding + /// + public Enums.Coding Coding => (Enums.Coding)Get("coding"); + + /// + /// Pixel interpretation + /// + public Enums.Interpretation Interpretation => (Enums.Interpretation)Get("interpretation"); + + /// + /// Horizontal offset of origin + /// + public int Xoffset => (int)Get("xoffset"); + + /// + /// Vertical offset of origin + /// + public int Yoffset => (int)Get("yoffset"); + + /// + /// Horizontal resolution in pixels/mm + /// + public double Xres => (double)Get("xres"); + + /// + /// Vertical resolution in pixels/mm + /// + public double Yres => (double)Get("yres"); + + /// + /// Image filename + /// + public string Filename => (string)Get("filename"); + + #endregion +} \ No newline at end of file diff --git a/src/NetVips/Image.Operators.cs b/src/NetVips/Image.Operators.cs index 82f795ba..91b29df3 100644 --- a/src/NetVips/Image.Operators.cs +++ b/src/NetVips/Image.Operators.cs @@ -7,865 +7,864 @@ // //------------------------------------------------------------------------------ -namespace NetVips +namespace NetVips; + +public partial class Image { - public partial class Image - { - #region auto-generated operator overloads - - /// - /// This operation calculates + . - /// - /// Left . - /// Right . - /// A new . - public static Image operator +(Image left, Image right) => - left.Call("add", right) as Image; - - /// - /// This operation calculates + . - /// - /// Left double constant. - /// Right . - /// A new . - public static Image operator +(double left, Image right) => - right.Call("linear", 1.0, left) as Image; - - /// - /// This operation calculates + . - /// - /// Left . - /// Right double constant. - /// A new . - public static Image operator +(Image left, double right) => - left.Call("linear", 1.0, right) as Image; - - /// - /// This operation calculates + . - /// - /// Left double array. - /// Right . - /// A new . - public static Image operator +(double[] left, Image right) => - right.Call("linear", 1.0, left) as Image; - - /// - /// This operation calculates + . - /// - /// Left . - /// Right double array. - /// A new . - public static Image operator +(Image left, double[] right) => - left.Call("linear", 1.0, right) as Image; - - /// - /// This operation calculates + . - /// - /// Left integer array. - /// Right . - /// A new . - public static Image operator +(int[] left, Image right) => - right.Call("linear", 1.0, left) as Image; - - /// - /// This operation calculates + . - /// - /// Left . - /// Right integer array. - /// A new . - public static Image operator +(Image left, int[] right) => - left.Call("linear", 1.0, right) as Image; - - /// - /// This operation calculates - . - /// - /// Left . - /// Right . - /// A new . - public static Image operator -(Image left, Image right) => - left.Call("subtract", right) as Image; - - /// - /// This operation calculates - . - /// - /// Left double constant. - /// Right . - /// A new . - public static Image operator -(double left, Image right) => - right.Call("linear", -1.0, left) as Image; - - /// - /// This operation calculates - . - /// - /// Left . - /// Right double constant. - /// A new . - public static Image operator -(Image left, double right) => - left.Call("linear", 1.0, -right) as Image; - - /// - /// This operation calculates - . - /// - /// Left double array. - /// Right . - /// A new . - public static Image operator -(double[] left, Image right) => - right.Call("linear", -1.0, left) as Image; - - /// - /// This operation calculates - . - /// - /// Left . - /// Right double array. - /// A new . - public static Image operator -(Image left, double[] right) => - left.Call("linear", 1.0, right.Negate()) as Image; - - /// - /// This operation calculates - . - /// - /// Left integer array. - /// Right . - /// A new . - public static Image operator -(int[] left, Image right) => - right.Call("linear", -1.0, left) as Image; - - /// - /// This operation calculates - . - /// - /// Left . - /// Right integer array. - /// A new . - public static Image operator -(Image left, int[] right) => - left.Call("linear", 1.0, right.Negate()) as Image; - - /// - /// This operation calculates * . - /// - /// Left . - /// Right . - /// A new . - public static Image operator *(Image left, Image right) => - left.Call("multiply", right) as Image; - - /// - /// This operation calculates * . - /// - /// Left double constant. - /// Right . - /// A new . - public static Image operator *(double left, Image right) => - right.Call("linear", left, 0.0) as Image; - - /// - /// This operation calculates * . - /// - /// Left . - /// Right double constant. - /// A new . - public static Image operator *(Image left, double right) => - left.Call("linear", right, 0.0) as Image; - - /// - /// This operation calculates * . - /// - /// Left double array. - /// Right . - /// A new . - public static Image operator *(double[] left, Image right) => - right.Call("linear", left, 0.0) as Image; - - /// - /// This operation calculates * . - /// - /// Left . - /// Right double array. - /// A new . - public static Image operator *(Image left, double[] right) => - left.Call("linear", right, 0.0) as Image; - - /// - /// This operation calculates * . - /// - /// Left integer array. - /// Right . - /// A new . - public static Image operator *(int[] left, Image right) => - right.Call("linear", left, 0.0) as Image; - - /// - /// This operation calculates * . - /// - /// Left . - /// Right integer array. - /// A new . - public static Image operator *(Image left, int[] right) => - left.Call("linear", right, 0.0) as Image; - - /// - /// This operation calculates / . - /// - /// Left . - /// Right . - /// A new . - public static Image operator /(Image left, Image right) => - left.Call("divide", right) as Image; - - /// - /// This operation calculates / . - /// - /// Left double constant. - /// Right . - /// A new . - public static Image operator /(double left, Image right) => - right.Pow(-1.0).Call("linear", left, 0.0) as Image; - - /// - /// This operation calculates / . - /// - /// Left . - /// Right double constant. - /// A new . - public static Image operator /(Image left, double right) => - left.Call("linear", 1.0 / right, 0.0) as Image; - - /// - /// This operation calculates / . - /// - /// Left double array. - /// Right . - /// A new . - public static Image operator /(double[] left, Image right) => - right.Pow(-1.0).Call("linear", left, 0.0) as Image; - - /// - /// This operation calculates / . - /// - /// Left . - /// Right double array. - /// A new . - public static Image operator /(Image left, double[] right) => - left.Call("linear", right.Invert(), 0.0) as Image; - - /// - /// This operation calculates / . - /// - /// Left integer array. - /// Right . - /// A new . - public static Image operator /(int[] left, Image right) => - right.Pow(-1.0).Call("linear", left, 0.0) as Image; - - /// - /// This operation calculates / . - /// - /// Left . - /// Right integer array. - /// A new . - public static Image operator /(Image left, int[] right) => - left.Call("linear", right.Invert(), 0.0) as Image; - - /// - /// This operation calculates % - /// (remainder after integer division). - /// - /// Left . - /// Right . - /// A new . - public static Image operator %(Image left, Image right) => - left.Call("remainder", right) as Image; - - /// - /// This operation calculates % - /// (remainder after integer division). - /// - /// Left double constant. - /// Right . - /// A new . - public static Image operator %(double left, Image right) => - right.Call("remainder_const", left) as Image; - - /// - /// This operation calculates % - /// (remainder after integer division). - /// - /// Left . - /// Right double constant. - /// A new . - public static Image operator %(Image left, double right) => - left.Call("remainder_const", right) as Image; - - /// - /// This operation calculates % - /// (remainder after integer division). - /// - /// Left double array. - /// Right . - /// A new . - public static Image operator %(double[] left, Image right) => - right.Call("remainder_const", left) as Image; - - /// - /// This operation calculates % - /// (remainder after integer division). - /// - /// Left . - /// Right double array. - /// A new . - public static Image operator %(Image left, double[] right) => - left.Call("remainder_const", right) as Image; - - /// - /// This operation calculates % - /// (remainder after integer division). - /// - /// Left integer array. - /// Right . - /// A new . - public static Image operator %(int[] left, Image right) => - right.Call("remainder_const", left) as Image; - - /// - /// This operation calculates % - /// (remainder after integer division). - /// - /// Left . - /// Right integer array. - /// A new . - public static Image operator %(Image left, int[] right) => - left.Call("remainder_const", right) as Image; - - /// - /// This operation computes the logical bitwise AND of its operands. - /// - /// Left . - /// Right . - /// A new . - public static Image operator &(Image left, Image right) => - left.Call("boolean", right, Enums.OperationBoolean.And) as Image; - - /// - /// This operation computes the logical bitwise AND of its operands. - /// - /// Left double constant. - /// Right . - /// A new . - public static Image operator &(double left, Image right) => - right.Call("boolean_const", Enums.OperationBoolean.And, left) as Image; - - /// - /// This operation computes the logical bitwise AND of its operands. - /// - /// Left . - /// Right double constant. - /// A new . - public static Image operator &(Image left, double right) => - left.Call("boolean_const", Enums.OperationBoolean.And, right) as Image; - - /// - /// This operation computes the logical bitwise AND of its operands. - /// - /// Left double array. - /// Right . - /// A new . - public static Image operator &(double[] left, Image right) => - right.Call("boolean_const", Enums.OperationBoolean.And, left) as Image; - - /// - /// This operation computes the logical bitwise AND of its operands. - /// - /// Left . - /// Right double array. - /// A new . - public static Image operator &(Image left, double[] right) => - left.Call("boolean_const", Enums.OperationBoolean.And, right) as Image; - - /// - /// This operation computes the logical bitwise AND of its operands. - /// - /// Left integer array. - /// Right . - /// A new . - public static Image operator &(int[] left, Image right) => - right.Call("boolean_const", Enums.OperationBoolean.And, left) as Image; - - /// - /// This operation computes the logical bitwise AND of its operands. - /// - /// Left . - /// Right integer array. - /// A new . - public static Image operator &(Image left, int[] right) => - left.Call("boolean_const", Enums.OperationBoolean.And, right) as Image; - - /// - /// This operation computes the bitwise OR of its operands. - /// - /// Left . - /// Right . - /// A new . - public static Image operator |(Image left, Image right) => - left.Call("boolean", right, Enums.OperationBoolean.Or) as Image; - - /// - /// This operation computes the bitwise OR of its operands. - /// - /// Left double constant. - /// Right . - /// A new . - public static Image operator |(double left, Image right) => - right.Call("boolean_const", Enums.OperationBoolean.Or, left) as Image; - - /// - /// This operation computes the bitwise OR of its operands. - /// - /// Left . - /// Right double constant. - /// A new . - public static Image operator |(Image left, double right) => - left.Call("boolean_const", Enums.OperationBoolean.Or, right) as Image; - - /// - /// This operation computes the bitwise OR of its operands. - /// - /// Left double array. - /// Right . - /// A new . - public static Image operator |(double[] left, Image right) => - right.Call("boolean_const", Enums.OperationBoolean.Or, left) as Image; - - /// - /// This operation computes the bitwise OR of its operands. - /// - /// Left . - /// Right double array. - /// A new . - public static Image operator |(Image left, double[] right) => - left.Call("boolean_const", Enums.OperationBoolean.Or, right) as Image; - - /// - /// This operation computes the bitwise OR of its operands. - /// - /// Left integer array. - /// Right . - /// A new . - public static Image operator |(int[] left, Image right) => - right.Call("boolean_const", Enums.OperationBoolean.Or, left) as Image; - - /// - /// This operation computes the bitwise OR of its operands. - /// - /// Left . - /// Right integer array. - /// A new . - public static Image operator |(Image left, int[] right) => - left.Call("boolean_const", Enums.OperationBoolean.Or, right) as Image; - - /// - /// This operation computes the bitwise exclusive-OR of its operands. - /// - /// Left . - /// Right . - /// A new . - public static Image operator ^(Image left, Image right) => - left.Call("boolean", right, Enums.OperationBoolean.Eor) as Image; - - /// - /// This operation computes the bitwise exclusive-OR of its operands. - /// - /// Left double constant. - /// Right . - /// A new . - public static Image operator ^(double left, Image right) => - right.Call("boolean_const", Enums.OperationBoolean.Eor, left) as Image; - - /// - /// This operation computes the bitwise exclusive-OR of its operands. - /// - /// Left . - /// Right double constant. - /// A new . - public static Image operator ^(Image left, double right) => - left.Call("boolean_const", Enums.OperationBoolean.Eor, right) as Image; - - /// - /// This operation computes the bitwise exclusive-OR of its operands. - /// - /// Left double array. - /// Right . - /// A new . - public static Image operator ^(double[] left, Image right) => - right.Call("boolean_const", Enums.OperationBoolean.Eor, left) as Image; - - /// - /// This operation computes the bitwise exclusive-OR of its operands. - /// - /// Left . - /// Right double array. - /// A new . - public static Image operator ^(Image left, double[] right) => - left.Call("boolean_const", Enums.OperationBoolean.Eor, right) as Image; - - /// - /// This operation computes the bitwise exclusive-OR of its operands. - /// - /// Left integer array. - /// Right . - /// A new . - public static Image operator ^(int[] left, Image right) => - right.Call("boolean_const", Enums.OperationBoolean.Eor, left) as Image; - - /// - /// This operation computes the bitwise exclusive-OR of its operands. - /// - /// Left . - /// Right integer array. - /// A new . - public static Image operator ^(Image left, int[] right) => - left.Call("boolean_const", Enums.OperationBoolean.Eor, right) as Image; - - /// - /// This operation shifts its first operand left by the number of bits specified by its second operand. - /// - /// Left . - /// The number of bits. - /// A new . - public static Image operator <<(Image left, int right) => - left.Call("boolean_const", Enums.OperationBoolean.Lshift, right) as Image; - - /// - /// This operation shifts its first operand right by the number of bits specified by its second operand. - /// - /// Left . - /// The number of bits. - /// A new . - public static Image operator >>(Image left, int right) => - left.Call("boolean_const", Enums.OperationBoolean.Rshift, right) as Image; - - /// - /// This operation compares two images on equality. - /// - /// Left double constant to compare. - /// Right to compare. - /// A new . - public static Image operator ==(double left, Image right) => - right.Call("relational_const", Enums.OperationRelational.Equal, left) as Image; - - /// - /// This operation compares two images on equality. - /// - /// Left double array to compare. - /// Right to compare. - /// A new . - public static Image operator ==(double[] left, Image right) => - right.Call("relational_const", Enums.OperationRelational.Equal, left) as Image; - - /// - /// This operation compares two images on equality. - /// - /// Left integer array to compare. - /// Right to compare. - /// A new . - public static Image operator ==(int[] left, Image right) => - right.Call("relational_const", Enums.OperationRelational.Equal, left) as Image; - - /// - /// This operation compares two images on inequality. - /// - /// Left double constant to compare. - /// Right to compare. - /// A new . - public static Image operator !=(double left, Image right) => - right.Call("relational_const", Enums.OperationRelational.Noteq, left) as Image; - - /// - /// This operation compares two images on inequality. - /// - /// Left double array to compare. - /// Right to compare. - /// A new . - public static Image operator !=(double[] left, Image right) => - right.Call("relational_const", Enums.OperationRelational.Noteq, left) as Image; - - /// - /// This operation compares two images on inequality. - /// - /// Left integer array to compare. - /// Right to compare. - /// A new . - public static Image operator !=(int[] left, Image right) => - right.Call("relational_const", Enums.OperationRelational.Noteq, left) as Image; - - /// - /// This operation compares if the left operand is less than the right operand. - /// - /// Left . - /// Right . - /// A new . - public static Image operator <(Image left, Image right) => - left.Call("relational", right, Enums.OperationRelational.Less) as Image; - - /// - /// This operation compares if the left operand is less than the right operand. - /// - /// Left double constant. - /// Right . - /// A new . - public static Image operator <(double left, Image right) => - right.Call("relational_const", Enums.OperationRelational.More, left) as Image; - - /// - /// This operation compares if the left operand is less than the right operand. - /// - /// Left . - /// Right double constant. - /// A new . - public static Image operator <(Image left, double right) => - left.Call("relational_const", Enums.OperationRelational.Less, right) as Image; - - /// - /// This operation compares if the left operand is less than the right operand. - /// - /// Left double array. - /// Right . - /// A new . - public static Image operator <(double[] left, Image right) => - right.Call("relational_const", Enums.OperationRelational.More, left) as Image; - - /// - /// This operation compares if the left operand is less than the right operand. - /// - /// Left . - /// Right double array. - /// A new . - public static Image operator <(Image left, double[] right) => - left.Call("relational_const", Enums.OperationRelational.Less, right) as Image; - - /// - /// This operation compares if the left operand is less than the right operand. - /// - /// Left integer array. - /// Right . - /// A new . - public static Image operator <(int[] left, Image right) => - right.Call("relational_const", Enums.OperationRelational.More, left) as Image; - - /// - /// This operation compares if the left operand is less than the right operand. - /// - /// Left . - /// Right integer array. - /// A new . - public static Image operator <(Image left, int[] right) => - left.Call("relational_const", Enums.OperationRelational.Less, right) as Image; - - /// - /// This operation compares if the left operand is greater than the right operand. - /// - /// Left . - /// Right . - /// A new . - public static Image operator >(Image left, Image right) => - left.Call("relational", right, Enums.OperationRelational.More) as Image; - - /// - /// This operation compares if the left operand is greater than the right operand. - /// - /// Left double constant. - /// Right . - /// A new . - public static Image operator >(double left, Image right) => - right.Call("relational_const", Enums.OperationRelational.Less, left) as Image; - - /// - /// This operation compares if the left operand is greater than the right operand. - /// - /// Left . - /// Right double constant. - /// A new . - public static Image operator >(Image left, double right) => - left.Call("relational_const", Enums.OperationRelational.More, right) as Image; - - /// - /// This operation compares if the left operand is greater than the right operand. - /// - /// Left double array. - /// Right . - /// A new . - public static Image operator >(double[] left, Image right) => - right.Call("relational_const", Enums.OperationRelational.Less, left) as Image; - - /// - /// This operation compares if the left operand is greater than the right operand. - /// - /// Left . - /// Right double array. - /// A new . - public static Image operator >(Image left, double[] right) => - left.Call("relational_const", Enums.OperationRelational.More, right) as Image; - - /// - /// This operation compares if the left operand is greater than the right operand. - /// - /// Left integer array. - /// Right . - /// A new . - public static Image operator >(int[] left, Image right) => - right.Call("relational_const", Enums.OperationRelational.Less, left) as Image; - - /// - /// This operation compares if the left operand is greater than the right operand. - /// - /// Left . - /// Right integer array. - /// A new . - public static Image operator >(Image left, int[] right) => - left.Call("relational_const", Enums.OperationRelational.More, right) as Image; - - /// - /// This operation compares if the left operand is less than or equal to the right operand. - /// - /// Left . - /// Right . - /// A new . - public static Image operator <=(Image left, Image right) => - left.Call("relational", right, Enums.OperationRelational.Lesseq) as Image; - - /// - /// This operation compares if the left operand is less than or equal to the right operand. - /// - /// Left double constant. - /// Right . - /// A new . - public static Image operator <=(double left, Image right) => - right.Call("relational_const", Enums.OperationRelational.Moreeq, left) as Image; - - /// - /// This operation compares if the left operand is less than or equal to the right operand. - /// - /// Left . - /// Right double constant. - /// A new . - public static Image operator <=(Image left, double right) => - left.Call("relational_const", Enums.OperationRelational.Lesseq, right) as Image; - - /// - /// This operation compares if the left operand is less than or equal to the right operand. - /// - /// Left double array. - /// Right . - /// A new . - public static Image operator <=(double[] left, Image right) => - right.Call("relational_const", Enums.OperationRelational.Moreeq, left) as Image; - - /// - /// This operation compares if the left operand is less than or equal to the right operand. - /// - /// Left . - /// Right double array. - /// A new . - public static Image operator <=(Image left, double[] right) => - left.Call("relational_const", Enums.OperationRelational.Lesseq, right) as Image; - - /// - /// This operation compares if the left operand is less than or equal to the right operand. - /// - /// Left integer array. - /// Right . - /// A new . - public static Image operator <=(int[] left, Image right) => - right.Call("relational_const", Enums.OperationRelational.Moreeq, left) as Image; - - /// - /// This operation compares if the left operand is less than or equal to the right operand. - /// - /// Left . - /// Right integer array. - /// A new . - public static Image operator <=(Image left, int[] right) => - left.Call("relational_const", Enums.OperationRelational.Lesseq, right) as Image; - - /// - /// This operation compares if the left operand is greater than or equal to the right operand. - /// - /// Left . - /// Right . - /// A new . - public static Image operator >=(Image left, Image right) => - left.Call("relational", right, Enums.OperationRelational.Moreeq) as Image; - - /// - /// This operation compares if the left operand is greater than or equal to the right operand. - /// - /// Left double constant. - /// Right . - /// A new . - public static Image operator >=(double left, Image right) => - right.Call("relational_const", Enums.OperationRelational.Lesseq, left) as Image; - - /// - /// This operation compares if the left operand is greater than or equal to the right operand. - /// - /// Left . - /// Right double constant. - /// A new . - public static Image operator >=(Image left, double right) => - left.Call("relational_const", Enums.OperationRelational.Moreeq, right) as Image; - - /// - /// This operation compares if the left operand is greater than or equal to the right operand. - /// - /// Left double array. - /// Right . - /// A new . - public static Image operator >=(double[] left, Image right) => - right.Call("relational_const", Enums.OperationRelational.Lesseq, left) as Image; - - /// - /// This operation compares if the left operand is greater than or equal to the right operand. - /// - /// Left . - /// Right double array. - /// A new . - public static Image operator >=(Image left, double[] right) => - left.Call("relational_const", Enums.OperationRelational.Moreeq, right) as Image; - - /// - /// This operation compares if the left operand is greater than or equal to the right operand. - /// - /// Left integer array. - /// Right . - /// A new . - public static Image operator >=(int[] left, Image right) => - right.Call("relational_const", Enums.OperationRelational.Lesseq, left) as Image; - - /// - /// This operation compares if the left operand is greater than or equal to the right operand. - /// - /// Left . - /// Right integer array. - /// A new . - public static Image operator >=(Image left, int[] right) => - left.Call("relational_const", Enums.OperationRelational.Moreeq, right) as Image; - - /// - /// Returns a value indicating whether a given is definitely . - /// - /// The image to check. - /// if is definitely ; otherwise, . - public static bool operator true(Image image) => - // Always evaluate to false so that each side of the && equation is evaluated - false; - - /// - /// Returns a value indicating whether a given is definitely . - /// - /// The image to check. - /// if is definitely ; otherwise, . - public static bool operator false(Image image) => - // Always evaluate to false so that each side of the && equation is evaluated - false; - - #endregion - } -} + #region auto-generated operator overloads + + /// + /// This operation calculates + . + /// + /// Left . + /// Right . + /// A new . + public static Image operator +(Image left, Image right) => + left.Call("add", right) as Image; + + /// + /// This operation calculates + . + /// + /// Left double constant. + /// Right . + /// A new . + public static Image operator +(double left, Image right) => + right.Call("linear", 1.0, left) as Image; + + /// + /// This operation calculates + . + /// + /// Left . + /// Right double constant. + /// A new . + public static Image operator +(Image left, double right) => + left.Call("linear", 1.0, right) as Image; + + /// + /// This operation calculates + . + /// + /// Left double array. + /// Right . + /// A new . + public static Image operator +(double[] left, Image right) => + right.Call("linear", 1.0, left) as Image; + + /// + /// This operation calculates + . + /// + /// Left . + /// Right double array. + /// A new . + public static Image operator +(Image left, double[] right) => + left.Call("linear", 1.0, right) as Image; + + /// + /// This operation calculates + . + /// + /// Left integer array. + /// Right . + /// A new . + public static Image operator +(int[] left, Image right) => + right.Call("linear", 1.0, left) as Image; + + /// + /// This operation calculates + . + /// + /// Left . + /// Right integer array. + /// A new . + public static Image operator +(Image left, int[] right) => + left.Call("linear", 1.0, right) as Image; + + /// + /// This operation calculates - . + /// + /// Left . + /// Right . + /// A new . + public static Image operator -(Image left, Image right) => + left.Call("subtract", right) as Image; + + /// + /// This operation calculates - . + /// + /// Left double constant. + /// Right . + /// A new . + public static Image operator -(double left, Image right) => + right.Call("linear", -1.0, left) as Image; + + /// + /// This operation calculates - . + /// + /// Left . + /// Right double constant. + /// A new . + public static Image operator -(Image left, double right) => + left.Call("linear", 1.0, -right) as Image; + + /// + /// This operation calculates - . + /// + /// Left double array. + /// Right . + /// A new . + public static Image operator -(double[] left, Image right) => + right.Call("linear", -1.0, left) as Image; + + /// + /// This operation calculates - . + /// + /// Left . + /// Right double array. + /// A new . + public static Image operator -(Image left, double[] right) => + left.Call("linear", 1.0, right.Negate()) as Image; + + /// + /// This operation calculates - . + /// + /// Left integer array. + /// Right . + /// A new . + public static Image operator -(int[] left, Image right) => + right.Call("linear", -1.0, left) as Image; + + /// + /// This operation calculates - . + /// + /// Left . + /// Right integer array. + /// A new . + public static Image operator -(Image left, int[] right) => + left.Call("linear", 1.0, right.Negate()) as Image; + + /// + /// This operation calculates * . + /// + /// Left . + /// Right . + /// A new . + public static Image operator *(Image left, Image right) => + left.Call("multiply", right) as Image; + + /// + /// This operation calculates * . + /// + /// Left double constant. + /// Right . + /// A new . + public static Image operator *(double left, Image right) => + right.Call("linear", left, 0.0) as Image; + + /// + /// This operation calculates * . + /// + /// Left . + /// Right double constant. + /// A new . + public static Image operator *(Image left, double right) => + left.Call("linear", right, 0.0) as Image; + + /// + /// This operation calculates * . + /// + /// Left double array. + /// Right . + /// A new . + public static Image operator *(double[] left, Image right) => + right.Call("linear", left, 0.0) as Image; + + /// + /// This operation calculates * . + /// + /// Left . + /// Right double array. + /// A new . + public static Image operator *(Image left, double[] right) => + left.Call("linear", right, 0.0) as Image; + + /// + /// This operation calculates * . + /// + /// Left integer array. + /// Right . + /// A new . + public static Image operator *(int[] left, Image right) => + right.Call("linear", left, 0.0) as Image; + + /// + /// This operation calculates * . + /// + /// Left . + /// Right integer array. + /// A new . + public static Image operator *(Image left, int[] right) => + left.Call("linear", right, 0.0) as Image; + + /// + /// This operation calculates / . + /// + /// Left . + /// Right . + /// A new . + public static Image operator /(Image left, Image right) => + left.Call("divide", right) as Image; + + /// + /// This operation calculates / . + /// + /// Left double constant. + /// Right . + /// A new . + public static Image operator /(double left, Image right) => + right.Pow(-1.0).Call("linear", left, 0.0) as Image; + + /// + /// This operation calculates / . + /// + /// Left . + /// Right double constant. + /// A new . + public static Image operator /(Image left, double right) => + left.Call("linear", 1.0 / right, 0.0) as Image; + + /// + /// This operation calculates / . + /// + /// Left double array. + /// Right . + /// A new . + public static Image operator /(double[] left, Image right) => + right.Pow(-1.0).Call("linear", left, 0.0) as Image; + + /// + /// This operation calculates / . + /// + /// Left . + /// Right double array. + /// A new . + public static Image operator /(Image left, double[] right) => + left.Call("linear", right.Invert(), 0.0) as Image; + + /// + /// This operation calculates / . + /// + /// Left integer array. + /// Right . + /// A new . + public static Image operator /(int[] left, Image right) => + right.Pow(-1.0).Call("linear", left, 0.0) as Image; + + /// + /// This operation calculates / . + /// + /// Left . + /// Right integer array. + /// A new . + public static Image operator /(Image left, int[] right) => + left.Call("linear", right.Invert(), 0.0) as Image; + + /// + /// This operation calculates % + /// (remainder after integer division). + /// + /// Left . + /// Right . + /// A new . + public static Image operator %(Image left, Image right) => + left.Call("remainder", right) as Image; + + /// + /// This operation calculates % + /// (remainder after integer division). + /// + /// Left double constant. + /// Right . + /// A new . + public static Image operator %(double left, Image right) => + right.Call("remainder_const", left) as Image; + + /// + /// This operation calculates % + /// (remainder after integer division). + /// + /// Left . + /// Right double constant. + /// A new . + public static Image operator %(Image left, double right) => + left.Call("remainder_const", right) as Image; + + /// + /// This operation calculates % + /// (remainder after integer division). + /// + /// Left double array. + /// Right . + /// A new . + public static Image operator %(double[] left, Image right) => + right.Call("remainder_const", left) as Image; + + /// + /// This operation calculates % + /// (remainder after integer division). + /// + /// Left . + /// Right double array. + /// A new . + public static Image operator %(Image left, double[] right) => + left.Call("remainder_const", right) as Image; + + /// + /// This operation calculates % + /// (remainder after integer division). + /// + /// Left integer array. + /// Right . + /// A new . + public static Image operator %(int[] left, Image right) => + right.Call("remainder_const", left) as Image; + + /// + /// This operation calculates % + /// (remainder after integer division). + /// + /// Left . + /// Right integer array. + /// A new . + public static Image operator %(Image left, int[] right) => + left.Call("remainder_const", right) as Image; + + /// + /// This operation computes the logical bitwise AND of its operands. + /// + /// Left . + /// Right . + /// A new . + public static Image operator &(Image left, Image right) => + left.Call("boolean", right, Enums.OperationBoolean.And) as Image; + + /// + /// This operation computes the logical bitwise AND of its operands. + /// + /// Left double constant. + /// Right . + /// A new . + public static Image operator &(double left, Image right) => + right.Call("boolean_const", Enums.OperationBoolean.And, left) as Image; + + /// + /// This operation computes the logical bitwise AND of its operands. + /// + /// Left . + /// Right double constant. + /// A new . + public static Image operator &(Image left, double right) => + left.Call("boolean_const", Enums.OperationBoolean.And, right) as Image; + + /// + /// This operation computes the logical bitwise AND of its operands. + /// + /// Left double array. + /// Right . + /// A new . + public static Image operator &(double[] left, Image right) => + right.Call("boolean_const", Enums.OperationBoolean.And, left) as Image; + + /// + /// This operation computes the logical bitwise AND of its operands. + /// + /// Left . + /// Right double array. + /// A new . + public static Image operator &(Image left, double[] right) => + left.Call("boolean_const", Enums.OperationBoolean.And, right) as Image; + + /// + /// This operation computes the logical bitwise AND of its operands. + /// + /// Left integer array. + /// Right . + /// A new . + public static Image operator &(int[] left, Image right) => + right.Call("boolean_const", Enums.OperationBoolean.And, left) as Image; + + /// + /// This operation computes the logical bitwise AND of its operands. + /// + /// Left . + /// Right integer array. + /// A new . + public static Image operator &(Image left, int[] right) => + left.Call("boolean_const", Enums.OperationBoolean.And, right) as Image; + + /// + /// This operation computes the bitwise OR of its operands. + /// + /// Left . + /// Right . + /// A new . + public static Image operator |(Image left, Image right) => + left.Call("boolean", right, Enums.OperationBoolean.Or) as Image; + + /// + /// This operation computes the bitwise OR of its operands. + /// + /// Left double constant. + /// Right . + /// A new . + public static Image operator |(double left, Image right) => + right.Call("boolean_const", Enums.OperationBoolean.Or, left) as Image; + + /// + /// This operation computes the bitwise OR of its operands. + /// + /// Left . + /// Right double constant. + /// A new . + public static Image operator |(Image left, double right) => + left.Call("boolean_const", Enums.OperationBoolean.Or, right) as Image; + + /// + /// This operation computes the bitwise OR of its operands. + /// + /// Left double array. + /// Right . + /// A new . + public static Image operator |(double[] left, Image right) => + right.Call("boolean_const", Enums.OperationBoolean.Or, left) as Image; + + /// + /// This operation computes the bitwise OR of its operands. + /// + /// Left . + /// Right double array. + /// A new . + public static Image operator |(Image left, double[] right) => + left.Call("boolean_const", Enums.OperationBoolean.Or, right) as Image; + + /// + /// This operation computes the bitwise OR of its operands. + /// + /// Left integer array. + /// Right . + /// A new . + public static Image operator |(int[] left, Image right) => + right.Call("boolean_const", Enums.OperationBoolean.Or, left) as Image; + + /// + /// This operation computes the bitwise OR of its operands. + /// + /// Left . + /// Right integer array. + /// A new . + public static Image operator |(Image left, int[] right) => + left.Call("boolean_const", Enums.OperationBoolean.Or, right) as Image; + + /// + /// This operation computes the bitwise exclusive-OR of its operands. + /// + /// Left . + /// Right . + /// A new . + public static Image operator ^(Image left, Image right) => + left.Call("boolean", right, Enums.OperationBoolean.Eor) as Image; + + /// + /// This operation computes the bitwise exclusive-OR of its operands. + /// + /// Left double constant. + /// Right . + /// A new . + public static Image operator ^(double left, Image right) => + right.Call("boolean_const", Enums.OperationBoolean.Eor, left) as Image; + + /// + /// This operation computes the bitwise exclusive-OR of its operands. + /// + /// Left . + /// Right double constant. + /// A new . + public static Image operator ^(Image left, double right) => + left.Call("boolean_const", Enums.OperationBoolean.Eor, right) as Image; + + /// + /// This operation computes the bitwise exclusive-OR of its operands. + /// + /// Left double array. + /// Right . + /// A new . + public static Image operator ^(double[] left, Image right) => + right.Call("boolean_const", Enums.OperationBoolean.Eor, left) as Image; + + /// + /// This operation computes the bitwise exclusive-OR of its operands. + /// + /// Left . + /// Right double array. + /// A new . + public static Image operator ^(Image left, double[] right) => + left.Call("boolean_const", Enums.OperationBoolean.Eor, right) as Image; + + /// + /// This operation computes the bitwise exclusive-OR of its operands. + /// + /// Left integer array. + /// Right . + /// A new . + public static Image operator ^(int[] left, Image right) => + right.Call("boolean_const", Enums.OperationBoolean.Eor, left) as Image; + + /// + /// This operation computes the bitwise exclusive-OR of its operands. + /// + /// Left . + /// Right integer array. + /// A new . + public static Image operator ^(Image left, int[] right) => + left.Call("boolean_const", Enums.OperationBoolean.Eor, right) as Image; + + /// + /// This operation shifts its first operand left by the number of bits specified by its second operand. + /// + /// Left . + /// The number of bits. + /// A new . + public static Image operator <<(Image left, int right) => + left.Call("boolean_const", Enums.OperationBoolean.Lshift, right) as Image; + + /// + /// This operation shifts its first operand right by the number of bits specified by its second operand. + /// + /// Left . + /// The number of bits. + /// A new . + public static Image operator >>(Image left, int right) => + left.Call("boolean_const", Enums.OperationBoolean.Rshift, right) as Image; + + /// + /// This operation compares two images on equality. + /// + /// Left double constant to compare. + /// Right to compare. + /// A new . + public static Image operator ==(double left, Image right) => + right.Call("relational_const", Enums.OperationRelational.Equal, left) as Image; + + /// + /// This operation compares two images on equality. + /// + /// Left double array to compare. + /// Right to compare. + /// A new . + public static Image operator ==(double[] left, Image right) => + right.Call("relational_const", Enums.OperationRelational.Equal, left) as Image; + + /// + /// This operation compares two images on equality. + /// + /// Left integer array to compare. + /// Right to compare. + /// A new . + public static Image operator ==(int[] left, Image right) => + right.Call("relational_const", Enums.OperationRelational.Equal, left) as Image; + + /// + /// This operation compares two images on inequality. + /// + /// Left double constant to compare. + /// Right to compare. + /// A new . + public static Image operator !=(double left, Image right) => + right.Call("relational_const", Enums.OperationRelational.Noteq, left) as Image; + + /// + /// This operation compares two images on inequality. + /// + /// Left double array to compare. + /// Right to compare. + /// A new . + public static Image operator !=(double[] left, Image right) => + right.Call("relational_const", Enums.OperationRelational.Noteq, left) as Image; + + /// + /// This operation compares two images on inequality. + /// + /// Left integer array to compare. + /// Right to compare. + /// A new . + public static Image operator !=(int[] left, Image right) => + right.Call("relational_const", Enums.OperationRelational.Noteq, left) as Image; + + /// + /// This operation compares if the left operand is less than the right operand. + /// + /// Left . + /// Right . + /// A new . + public static Image operator <(Image left, Image right) => + left.Call("relational", right, Enums.OperationRelational.Less) as Image; + + /// + /// This operation compares if the left operand is less than the right operand. + /// + /// Left double constant. + /// Right . + /// A new . + public static Image operator <(double left, Image right) => + right.Call("relational_const", Enums.OperationRelational.More, left) as Image; + + /// + /// This operation compares if the left operand is less than the right operand. + /// + /// Left . + /// Right double constant. + /// A new . + public static Image operator <(Image left, double right) => + left.Call("relational_const", Enums.OperationRelational.Less, right) as Image; + + /// + /// This operation compares if the left operand is less than the right operand. + /// + /// Left double array. + /// Right . + /// A new . + public static Image operator <(double[] left, Image right) => + right.Call("relational_const", Enums.OperationRelational.More, left) as Image; + + /// + /// This operation compares if the left operand is less than the right operand. + /// + /// Left . + /// Right double array. + /// A new . + public static Image operator <(Image left, double[] right) => + left.Call("relational_const", Enums.OperationRelational.Less, right) as Image; + + /// + /// This operation compares if the left operand is less than the right operand. + /// + /// Left integer array. + /// Right . + /// A new . + public static Image operator <(int[] left, Image right) => + right.Call("relational_const", Enums.OperationRelational.More, left) as Image; + + /// + /// This operation compares if the left operand is less than the right operand. + /// + /// Left . + /// Right integer array. + /// A new . + public static Image operator <(Image left, int[] right) => + left.Call("relational_const", Enums.OperationRelational.Less, right) as Image; + + /// + /// This operation compares if the left operand is greater than the right operand. + /// + /// Left . + /// Right . + /// A new . + public static Image operator >(Image left, Image right) => + left.Call("relational", right, Enums.OperationRelational.More) as Image; + + /// + /// This operation compares if the left operand is greater than the right operand. + /// + /// Left double constant. + /// Right . + /// A new . + public static Image operator >(double left, Image right) => + right.Call("relational_const", Enums.OperationRelational.Less, left) as Image; + + /// + /// This operation compares if the left operand is greater than the right operand. + /// + /// Left . + /// Right double constant. + /// A new . + public static Image operator >(Image left, double right) => + left.Call("relational_const", Enums.OperationRelational.More, right) as Image; + + /// + /// This operation compares if the left operand is greater than the right operand. + /// + /// Left double array. + /// Right . + /// A new . + public static Image operator >(double[] left, Image right) => + right.Call("relational_const", Enums.OperationRelational.Less, left) as Image; + + /// + /// This operation compares if the left operand is greater than the right operand. + /// + /// Left . + /// Right double array. + /// A new . + public static Image operator >(Image left, double[] right) => + left.Call("relational_const", Enums.OperationRelational.More, right) as Image; + + /// + /// This operation compares if the left operand is greater than the right operand. + /// + /// Left integer array. + /// Right . + /// A new . + public static Image operator >(int[] left, Image right) => + right.Call("relational_const", Enums.OperationRelational.Less, left) as Image; + + /// + /// This operation compares if the left operand is greater than the right operand. + /// + /// Left . + /// Right integer array. + /// A new . + public static Image operator >(Image left, int[] right) => + left.Call("relational_const", Enums.OperationRelational.More, right) as Image; + + /// + /// This operation compares if the left operand is less than or equal to the right operand. + /// + /// Left . + /// Right . + /// A new . + public static Image operator <=(Image left, Image right) => + left.Call("relational", right, Enums.OperationRelational.Lesseq) as Image; + + /// + /// This operation compares if the left operand is less than or equal to the right operand. + /// + /// Left double constant. + /// Right . + /// A new . + public static Image operator <=(double left, Image right) => + right.Call("relational_const", Enums.OperationRelational.Moreeq, left) as Image; + + /// + /// This operation compares if the left operand is less than or equal to the right operand. + /// + /// Left . + /// Right double constant. + /// A new . + public static Image operator <=(Image left, double right) => + left.Call("relational_const", Enums.OperationRelational.Lesseq, right) as Image; + + /// + /// This operation compares if the left operand is less than or equal to the right operand. + /// + /// Left double array. + /// Right . + /// A new . + public static Image operator <=(double[] left, Image right) => + right.Call("relational_const", Enums.OperationRelational.Moreeq, left) as Image; + + /// + /// This operation compares if the left operand is less than or equal to the right operand. + /// + /// Left . + /// Right double array. + /// A new . + public static Image operator <=(Image left, double[] right) => + left.Call("relational_const", Enums.OperationRelational.Lesseq, right) as Image; + + /// + /// This operation compares if the left operand is less than or equal to the right operand. + /// + /// Left integer array. + /// Right . + /// A new . + public static Image operator <=(int[] left, Image right) => + right.Call("relational_const", Enums.OperationRelational.Moreeq, left) as Image; + + /// + /// This operation compares if the left operand is less than or equal to the right operand. + /// + /// Left . + /// Right integer array. + /// A new . + public static Image operator <=(Image left, int[] right) => + left.Call("relational_const", Enums.OperationRelational.Lesseq, right) as Image; + + /// + /// This operation compares if the left operand is greater than or equal to the right operand. + /// + /// Left . + /// Right . + /// A new . + public static Image operator >=(Image left, Image right) => + left.Call("relational", right, Enums.OperationRelational.Moreeq) as Image; + + /// + /// This operation compares if the left operand is greater than or equal to the right operand. + /// + /// Left double constant. + /// Right . + /// A new . + public static Image operator >=(double left, Image right) => + right.Call("relational_const", Enums.OperationRelational.Lesseq, left) as Image; + + /// + /// This operation compares if the left operand is greater than or equal to the right operand. + /// + /// Left . + /// Right double constant. + /// A new . + public static Image operator >=(Image left, double right) => + left.Call("relational_const", Enums.OperationRelational.Moreeq, right) as Image; + + /// + /// This operation compares if the left operand is greater than or equal to the right operand. + /// + /// Left double array. + /// Right . + /// A new . + public static Image operator >=(double[] left, Image right) => + right.Call("relational_const", Enums.OperationRelational.Lesseq, left) as Image; + + /// + /// This operation compares if the left operand is greater than or equal to the right operand. + /// + /// Left . + /// Right double array. + /// A new . + public static Image operator >=(Image left, double[] right) => + left.Call("relational_const", Enums.OperationRelational.Moreeq, right) as Image; + + /// + /// This operation compares if the left operand is greater than or equal to the right operand. + /// + /// Left integer array. + /// Right . + /// A new . + public static Image operator >=(int[] left, Image right) => + right.Call("relational_const", Enums.OperationRelational.Lesseq, left) as Image; + + /// + /// This operation compares if the left operand is greater than or equal to the right operand. + /// + /// Left . + /// Right integer array. + /// A new . + public static Image operator >=(Image left, int[] right) => + left.Call("relational_const", Enums.OperationRelational.Moreeq, right) as Image; + + /// + /// Returns a value indicating whether a given is definitely . + /// + /// The image to check. + /// if is definitely ; otherwise, . + public static bool operator true(Image image) => + // Always evaluate to false so that each side of the && equation is evaluated + false; + + /// + /// Returns a value indicating whether a given is definitely . + /// + /// The image to check. + /// if is definitely ; otherwise, . + public static bool operator false(Image image) => + // Always evaluate to false so that each side of the && equation is evaluated + false; + + #endregion +} \ No newline at end of file diff --git a/src/NetVips/Image.cs b/src/NetVips/Image.cs index 928eb26c..3d63bf05 100644 --- a/src/NetVips/Image.cs +++ b/src/NetVips/Image.cs @@ -1,2319 +1,2318 @@ -namespace NetVips +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; +using System.Threading; +using NetVips.Internal; + +namespace NetVips; + +/// +/// Wrap a object. +/// +public partial class Image : VipsObject { - using System; - using System.Collections.Generic; - using System.IO; - using System.Runtime.InteropServices; - using System.Security; - using System.Text; - using System.Threading; - using Internal; - /// - /// Wrap a object. + /// A evaluation delegate that can be used on the + /// , and + /// signals. /// - public partial class Image : VipsObject - { - /// - /// A evaluation delegate that can be used on the - /// , and - /// signals. - /// - /// - /// Use to enable progress reporting on an image. - /// - public delegate void EvalDelegate(Image image, VipsProgress progressStruct); - - /// - /// Internal marshaller delegate for . - /// - [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate void EvalMarshalDelegate(nint imagePtr, VipsProgress progress, nint userDataPtr); - - /// - internal Image(IntPtr pointer) - : base(pointer) - { - } - - #region helpers - - /// - /// Run a complex function on a non-complex image. - /// - /// - /// The image needs to be complex, or have an even number of bands. The input - /// can be int, the output is always float or double. - /// - /// A complex function. - /// A non-complex image. - /// A new . - /// If image doesn't have an even number of bands. - private static Image RunCmplx(Func func, Image image) - { - var originalFormat = image.Format; - if (image.Format != Enums.BandFormat.Complex && image.Format != Enums.BandFormat.Dpcomplex) - { - if (image.Bands % 2 != 0) - { - throw new ArgumentException("not an even number of bands"); - } + /// + /// Use to enable progress reporting on an image. + /// + public delegate void EvalDelegate(Image image, VipsProgress progressStruct); - if (image.Format != Enums.BandFormat.Float && image.Format != Enums.BandFormat.Double) - { - using (image) - { - image = image.Cast(Enums.BandFormat.Float); - } - } + /// + /// Internal marshaller delegate for . + /// + [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void EvalMarshalDelegate(nint imagePtr, VipsProgress progress, nint userDataPtr); - var newFormat = image.Format == Enums.BandFormat.Double - ? Enums.BandFormat.Dpcomplex - : Enums.BandFormat.Complex; + /// + internal Image(IntPtr pointer) + : base(pointer) + { + } - using (image) - { - image = image.Copy(format: newFormat, bands: image.Bands / 2); - } - } + #region helpers - using (image) + /// + /// Run a complex function on a non-complex image. + /// + /// + /// The image needs to be complex, or have an even number of bands. The input + /// can be int, the output is always float or double. + /// + /// A complex function. + /// A non-complex image. + /// A new . + /// If image doesn't have an even number of bands. + private static Image RunCmplx(Func func, Image image) + { + var originalFormat = image.Format; + if (image.Format != Enums.BandFormat.Complex && image.Format != Enums.BandFormat.Dpcomplex) + { + if (image.Bands % 2 != 0) { - image = func(image); + throw new ArgumentException("not an even number of bands"); } - if (originalFormat != Enums.BandFormat.Complex && originalFormat != Enums.BandFormat.Dpcomplex) + if (image.Format != Enums.BandFormat.Float && image.Format != Enums.BandFormat.Double) { - var newFormat = image.Format == Enums.BandFormat.Dpcomplex - ? Enums.BandFormat.Double - : Enums.BandFormat.Float; - using (image) { - image = image.Copy(format: newFormat, bands: image.Bands * 2); + image = image.Cast(Enums.BandFormat.Float); } } - return image; - } + var newFormat = image.Format == Enums.BandFormat.Double + ? Enums.BandFormat.Dpcomplex + : Enums.BandFormat.Complex; - /// - /// Turn a constant (eg. 1, "12", new[] {1, 2, 3}, new[] {new[] {1}}) into an image using - /// as a guide. - /// - /// Image guide. - /// A constant. - /// A new . - public static Image Imageize(Image matchImage, object value) - { - // careful! this can be None if value is a 2D array - return value switch + using (image) { - Image image => image, - double[,] doubleArray => NewFromArray(doubleArray), - int[,] intArray => NewFromArray(intArray), - double[] doubles => matchImage.NewFromImage(doubles), - int[] ints => matchImage.NewFromImage(ints), - double doubleValue => matchImage.NewFromImage(doubleValue), - int intValue => matchImage.NewFromImage(intValue), - _ => throw new ArgumentException($"unsupported value type {value.GetType()} for Imageize") - }; - } - - /// - /// Find the name of the load operation vips will use to load a file. - /// - /// - /// For example "VipsForeignLoadJpegFile". You can use this to work out what - /// options to pass to . - /// - /// The file to test. - /// The name of the load operation, or . - public static string FindLoad(string filename) - { - var bytes = Encoding.UTF8.GetBytes(filename + char.MinValue); // Ensure null-terminated string - return Marshal.PtrToStringAnsi(VipsForeign.FindLoad(bytes)); + image = image.Copy(format: newFormat, bands: image.Bands / 2); + } } - /// - /// Find the name of the load operation vips will use to load a buffer. - /// - /// - /// For example "VipsForeignLoadJpegBuffer". You can use this to work out what - /// options to pass to . - /// - /// The buffer to test. - /// The name of the load operation, or . - public static string FindLoadBuffer(byte[] data) => - Marshal.PtrToStringAnsi(VipsForeign.FindLoadBuffer(data, (ulong)data.Length)); - - /// - /// Find the name of the load operation vips will use to load a buffer. - /// - /// - /// For example "VipsForeignLoadJpegBuffer". You can use this to work out what - /// options to pass to . - /// - /// The buffer to test. - /// The name of the load operation, or . - public static string FindLoadBuffer(string data) => FindLoadBuffer(Encoding.UTF8.GetBytes(data)); - - /// - /// Find the name of the load operation vips will use to load a buffer. - /// - /// - /// For example "VipsForeignLoadJpegBuffer". You can use this to work out what - /// options to pass to . - /// - /// The buffer to test. - /// The name of the load operation, or . - public static string FindLoadBuffer(char[] data) => FindLoadBuffer(Encoding.UTF8.GetBytes(data)); - - /// - /// Find the name of the load operation vips will use to load a source. - /// - /// - /// For example "VipsForeignLoadJpegSource". You can use this to work out what - /// options to pass to . - /// - /// The source to test. - /// The name of the load operation, or . - public static string FindLoadSource(Source source) => - Marshal.PtrToStringAnsi(VipsForeign.FindLoadSource(source)); - - /// - /// Find the name of the load operation vips will use to load a stream. - /// - /// - /// For example "VipsForeignLoadJpegSource". You can use this to work out what - /// options to pass to . - /// - /// The stream to test. - /// The name of the load operation, or . - public static string FindLoadStream(Stream stream) - { - using var source = SourceStream.NewFromStream(stream); - return FindLoadSource(source); + using (image) + { + image = func(image); } - #endregion + if (originalFormat != Enums.BandFormat.Complex && originalFormat != Enums.BandFormat.Dpcomplex) + { + var newFormat = image.Format == Enums.BandFormat.Dpcomplex + ? Enums.BandFormat.Double + : Enums.BandFormat.Float; - #region constructors - - /// - /// Load an image from a file. - /// - /// - /// This method can load images in any format supported by vips. The - /// filename can include load options, for example: - /// - /// using var image = Image.NewFromFile("fred.jpg[shrink=2]"); - /// - /// You can also supply options as keyword arguments, for example: - /// - /// using var image = Image.NewFromFile("fred.jpg", new VOption - /// { - /// {"shrink", 2} - /// }); - /// - /// The full set of options available depend upon the load operation that - /// will be executed. Try something like: - /// - /// $ vips jpegload - /// - /// at the command-line to see a summary of the available options for the - /// JPEG loader. - /// - /// Loading is fast: only enough of the image is loaded to be able to fill - /// out the header. Pixels will only be decompressed when they are needed. - /// - /// The disc file to load the image from, with - /// optional appended arguments. - /// If set to , load the image - /// via memory rather than via a temporary disc file. See - /// for notes on where temporary files are created. Small images are loaded via memory - /// by default, use `VIPS_DISC_THRESHOLD` to set the definition of small. - /// Hint the expected access pattern for the image. - /// The type of error that will cause load to fail. By - /// default, loaders are permissive, that is, . - /// Optional options that depend on the load operation. - /// A new . - /// If unable to load from . - public static Image NewFromFile( - string vipsFilename, - bool? memory = null, - Enums.Access? access = null, - Enums.FailOn? failOn = null, - VOption kwargs = null) - { - var bytes = Encoding.UTF8.GetBytes(vipsFilename + char.MinValue); // Ensure null-terminated string - var filename = Vips.GetFilename(bytes); - var fileOptions = Vips.GetOptions(bytes).ToUtf8String(true); - - var operationName = Marshal.PtrToStringAnsi(VipsForeign.FindLoad(filename)); - if (operationName == null) + using (image) { - throw new VipsException($"unable to load from file {vipsFilename}"); + image = image.Copy(format: newFormat, bands: image.Bands * 2); } + } - var options = new VOption(); - if (kwargs != null) - { - options.Merge(kwargs); - } + return image; + } - options.AddIfPresent(nameof(memory), memory); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); + /// + /// Turn a constant (eg. 1, "12", new[] {1, 2, 3}, new[] {new[] {1}}) into an image using + /// as a guide. + /// + /// Image guide. + /// A constant. + /// A new . + public static Image Imageize(Image matchImage, object value) + { + // careful! this can be None if value is a 2D array + return value switch + { + Image image => image, + double[,] doubleArray => NewFromArray(doubleArray), + int[,] intArray => NewFromArray(intArray), + double[] doubles => matchImage.NewFromImage(doubles), + int[] ints => matchImage.NewFromImage(ints), + double doubleValue => matchImage.NewFromImage(doubleValue), + int intValue => matchImage.NewFromImage(intValue), + _ => throw new ArgumentException($"unsupported value type {value.GetType()} for Imageize") + }; + } - options.Add("string_options", fileOptions); + /// + /// Find the name of the load operation vips will use to load a file. + /// + /// + /// For example "VipsForeignLoadJpegFile". You can use this to work out what + /// options to pass to . + /// + /// The file to test. + /// The name of the load operation, or . + public static string FindLoad(string filename) + { + var bytes = Encoding.UTF8.GetBytes(filename + char.MinValue); // Ensure null-terminated string + return Marshal.PtrToStringAnsi(VipsForeign.FindLoad(bytes)); + } - return Operation.Call(operationName, options, filename.ToUtf8String(true)) as Image; - } + /// + /// Find the name of the load operation vips will use to load a buffer. + /// + /// + /// For example "VipsForeignLoadJpegBuffer". You can use this to work out what + /// options to pass to . + /// + /// The buffer to test. + /// The name of the load operation, or . + public static string FindLoadBuffer(byte[] data) => + Marshal.PtrToStringAnsi(VipsForeign.FindLoadBuffer(data, (ulong)data.Length)); - /// - /// Load a formatted image from memory. - /// - /// - /// This behaves exactly as , but the image is - /// loaded from the memory object rather than from a file. The memory - /// object can be a string or buffer. - /// - /// The memory object to load the image from. - /// Load options as a string. Use for no options. - /// Hint the expected access pattern for the image. - /// The type of error that will cause load to fail. By - /// default, loaders are permissive, that is, . - /// Optional options that depend on the load operation. - /// A new . - /// If unable to load from . - public static Image NewFromBuffer( - byte[] data, - string strOptions = "", - Enums.Access? access = null, - Enums.FailOn? failOn = null, - VOption kwargs = null) - { - var operationName = FindLoadBuffer(data); - if (operationName == null) - { - throw new VipsException("unable to load from buffer"); - } + /// + /// Find the name of the load operation vips will use to load a buffer. + /// + /// + /// For example "VipsForeignLoadJpegBuffer". You can use this to work out what + /// options to pass to . + /// + /// The buffer to test. + /// The name of the load operation, or . + public static string FindLoadBuffer(string data) => FindLoadBuffer(Encoding.UTF8.GetBytes(data)); - var options = new VOption(); - if (kwargs != null) - { - options.Merge(kwargs); - } + /// + /// Find the name of the load operation vips will use to load a buffer. + /// + /// + /// For example "VipsForeignLoadJpegBuffer". You can use this to work out what + /// options to pass to . + /// + /// The buffer to test. + /// The name of the load operation, or . + public static string FindLoadBuffer(char[] data) => FindLoadBuffer(Encoding.UTF8.GetBytes(data)); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); + /// + /// Find the name of the load operation vips will use to load a source. + /// + /// + /// For example "VipsForeignLoadJpegSource". You can use this to work out what + /// options to pass to . + /// + /// The source to test. + /// The name of the load operation, or . + public static string FindLoadSource(Source source) => + Marshal.PtrToStringAnsi(VipsForeign.FindLoadSource(source)); - options.Add("string_options", strOptions); + /// + /// Find the name of the load operation vips will use to load a stream. + /// + /// + /// For example "VipsForeignLoadJpegSource". You can use this to work out what + /// options to pass to . + /// + /// The stream to test. + /// The name of the load operation, or . + public static string FindLoadStream(Stream stream) + { + using var source = SourceStream.NewFromStream(stream); + return FindLoadSource(source); + } - return Operation.Call(operationName, options, data) as Image; - } + #endregion - /// - /// Load a formatted image from memory. - /// - /// - /// This behaves exactly as , but the image is - /// loaded from the memory object rather than from a file. The memory - /// object can be a string or buffer. - /// - /// The memory object to load the image from. - /// Load options as a string. Use for no options. - /// Hint the expected access pattern for the image. - /// The type of error that will cause load to fail. By - /// default, loaders are permissive, that is, . - /// Optional options that depend on the load operation. - /// A new . - /// If unable to load from . - public static Image NewFromBuffer( - string data, - string strOptions = "", - Enums.Access? access = null, - Enums.FailOn? failOn = null, - VOption kwargs = null) => NewFromBuffer(Encoding.UTF8.GetBytes(data), strOptions, access, failOn, kwargs); - - /// - /// Load a formatted image from memory. - /// - /// - /// This behaves exactly as , but the image is - /// loaded from the memory object rather than from a file. The memory - /// object can be a string or buffer. - /// - /// The memory object to load the image from. - /// Load options as a string. Use for no options. - /// Hint the expected access pattern for the image. - /// The type of error that will cause load to fail. By - /// default, loaders are permissive, that is, . - /// Optional options that depend on the load operation. - /// A new . - /// If unable to load from . - public static Image NewFromBuffer( - char[] data, - string strOptions = "", - Enums.Access? access = null, - Enums.FailOn? failOn = null, - VOption kwargs = null) => NewFromBuffer(Encoding.UTF8.GetBytes(data), strOptions, access, failOn, kwargs); - - /// - /// Load a formatted image from a source. - /// - /// - /// This behaves exactly as , but the image is - /// loaded from a source rather than from a file. - /// At least libvips 8.9 is needed. - /// - /// The source to load the image from. - /// Load options as a string. Use for no options. - /// Hint the expected access pattern for the image. - /// The type of error that will cause load to fail. By - /// default, loaders are permissive, that is, . - /// Optional options that depend on the load operation. - /// A new . - /// If unable to load from . - public static Image NewFromSource( - Source source, - string strOptions = "", - Enums.Access? access = null, - Enums.FailOn? failOn = null, - VOption kwargs = null) - { - // Load with the new source API if we can. Fall back to the older - // mechanism in case the loader we need has not been converted yet. - // We need to hide any errors from this first phase. - Vips.ErrorFreeze(); - var operationName = FindLoadSource(source); - Vips.ErrorThaw(); + #region constructors - var options = new VOption(); - if (kwargs != null) - { - options.Merge(kwargs); - } + /// + /// Load an image from a file. + /// + /// + /// This method can load images in any format supported by vips. The + /// filename can include load options, for example: + /// + /// using var image = Image.NewFromFile("fred.jpg[shrink=2]"); + /// + /// You can also supply options as keyword arguments, for example: + /// + /// using var image = Image.NewFromFile("fred.jpg", new VOption + /// { + /// {"shrink", 2} + /// }); + /// + /// The full set of options available depend upon the load operation that + /// will be executed. Try something like: + /// + /// $ vips jpegload + /// + /// at the command-line to see a summary of the available options for the + /// JPEG loader. + /// + /// Loading is fast: only enough of the image is loaded to be able to fill + /// out the header. Pixels will only be decompressed when they are needed. + /// + /// The disc file to load the image from, with + /// optional appended arguments. + /// If set to , load the image + /// via memory rather than via a temporary disc file. See + /// for notes on where temporary files are created. Small images are loaded via memory + /// by default, use `VIPS_DISC_THRESHOLD` to set the definition of small. + /// Hint the expected access pattern for the image. + /// The type of error that will cause load to fail. By + /// default, loaders are permissive, that is, . + /// Optional options that depend on the load operation. + /// A new . + /// If unable to load from . + public static Image NewFromFile( + string vipsFilename, + bool? memory = null, + Enums.Access? access = null, + Enums.FailOn? failOn = null, + VOption kwargs = null) + { + var bytes = Encoding.UTF8.GetBytes(vipsFilename + char.MinValue); // Ensure null-terminated string + var filename = Vips.GetFilename(bytes); + var fileOptions = Vips.GetOptions(bytes).ToUtf8String(true); - options.AddIfPresent(nameof(access), access); - options.AddFailOn(failOn); + var operationName = Marshal.PtrToStringAnsi(VipsForeign.FindLoad(filename)); + if (operationName == null) + { + throw new VipsException($"unable to load from file {vipsFilename}"); + } - options.Add("string_options", strOptions); + var options = new VOption(); + if (kwargs != null) + { + options.Merge(kwargs); + } - if (operationName != null) - { - return Operation.Call(operationName, options, source) as Image; - } + options.AddIfPresent(nameof(memory), memory); + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); - #region fallback mechanism + options.Add("string_options", fileOptions); - var filename = VipsConnection.FileName(source); - if (filename != IntPtr.Zero) - { - // Try with the old file-based loaders. - operationName = Marshal.PtrToStringAnsi(VipsForeign.FindLoad(filename)); - if (operationName == null) - { - throw new VipsException("unable to load from source"); - } + return Operation.Call(operationName, options, filename.ToUtf8String(true)) as Image; + } - return Operation.Call(operationName, options, filename.ToUtf8String()) as Image; - } + /// + /// Load a formatted image from memory. + /// + /// + /// This behaves exactly as , but the image is + /// loaded from the memory object rather than from a file. The memory + /// object can be a string or buffer. + /// + /// The memory object to load the image from. + /// Load options as a string. Use for no options. + /// Hint the expected access pattern for the image. + /// The type of error that will cause load to fail. By + /// default, loaders are permissive, that is, . + /// Optional options that depend on the load operation. + /// A new . + /// If unable to load from . + public static Image NewFromBuffer( + byte[] data, + string strOptions = "", + Enums.Access? access = null, + Enums.FailOn? failOn = null, + VOption kwargs = null) + { + var operationName = FindLoadBuffer(data); + if (operationName == null) + { + throw new VipsException("unable to load from buffer"); + } - // Try with the old buffer-based loaders. - // TODO: - // Do we need to check if the source can be efficiently mapped into - // memory with `vips_source_is_mappable`? - // This implicitly means that it will not work with network streams - // (`is_pipe` streams). + var options = new VOption(); + if (kwargs != null) + { + options.Merge(kwargs); + } - var ptr = VipsSource.MapBlob(source); - if (ptr == IntPtr.Zero) - { - throw new VipsException("unable to load from source"); - } + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); - using var blob = new VipsBlob(ptr); - var buf = blob.GetData(out var length); + options.Add("string_options", strOptions); - operationName = Marshal.PtrToStringAnsi(VipsForeign.FindLoadBuffer(buf, length)); - if (operationName == null) - { - throw new VipsException("unable to load from source"); - } + return Operation.Call(operationName, options, data) as Image; + } - return Operation.Call(operationName, options, blob) as Image; + /// + /// Load a formatted image from memory. + /// + /// + /// This behaves exactly as , but the image is + /// loaded from the memory object rather than from a file. The memory + /// object can be a string or buffer. + /// + /// The memory object to load the image from. + /// Load options as a string. Use for no options. + /// Hint the expected access pattern for the image. + /// The type of error that will cause load to fail. By + /// default, loaders are permissive, that is, . + /// Optional options that depend on the load operation. + /// A new . + /// If unable to load from . + public static Image NewFromBuffer( + string data, + string strOptions = "", + Enums.Access? access = null, + Enums.FailOn? failOn = null, + VOption kwargs = null) => NewFromBuffer(Encoding.UTF8.GetBytes(data), strOptions, access, failOn, kwargs); - #endregion - } + /// + /// Load a formatted image from memory. + /// + /// + /// This behaves exactly as , but the image is + /// loaded from the memory object rather than from a file. The memory + /// object can be a string or buffer. + /// + /// The memory object to load the image from. + /// Load options as a string. Use for no options. + /// Hint the expected access pattern for the image. + /// The type of error that will cause load to fail. By + /// default, loaders are permissive, that is, . + /// Optional options that depend on the load operation. + /// A new . + /// If unable to load from . + public static Image NewFromBuffer( + char[] data, + string strOptions = "", + Enums.Access? access = null, + Enums.FailOn? failOn = null, + VOption kwargs = null) => NewFromBuffer(Encoding.UTF8.GetBytes(data), strOptions, access, failOn, kwargs); - /// - /// Load a formatted image from a stream. - /// - /// - /// This behaves exactly as , but the image is - /// loaded from a stream rather than from a source. - /// At least libvips 8.9 is needed. - /// - /// The stream to load the image from. - /// Load options as a string. Use for no options. - /// Hint the expected access pattern for the image. - /// The type of error that will cause load to fail. By - /// default, loaders are permissive, that is, . - /// Optional options that depend on the load operation. - /// A new . - /// If unable to load from . - public static Image NewFromStream( - Stream stream, - string strOptions = "", - Enums.Access? access = null, - Enums.FailOn? failOn = null, - VOption kwargs = null) - { - var source = SourceStream.NewFromStream(stream); - var image = NewFromSource(source, strOptions, access, failOn, kwargs); - - // Need to dispose the SourceStream when the image is closed. - image.OnPostClose += () => source.Dispose(); - - return image; + /// + /// Load a formatted image from a source. + /// + /// + /// This behaves exactly as , but the image is + /// loaded from a source rather than from a file. + /// At least libvips 8.9 is needed. + /// + /// The source to load the image from. + /// Load options as a string. Use for no options. + /// Hint the expected access pattern for the image. + /// The type of error that will cause load to fail. By + /// default, loaders are permissive, that is, . + /// Optional options that depend on the load operation. + /// A new . + /// If unable to load from . + public static Image NewFromSource( + Source source, + string strOptions = "", + Enums.Access? access = null, + Enums.FailOn? failOn = null, + VOption kwargs = null) + { + // Load with the new source API if we can. Fall back to the older + // mechanism in case the loader we need has not been converted yet. + // We need to hide any errors from this first phase. + Vips.ErrorFreeze(); + var operationName = FindLoadSource(source); + Vips.ErrorThaw(); + + var options = new VOption(); + if (kwargs != null) + { + options.Merge(kwargs); } + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); - /// - /// Create an image from a 2D array. - /// - /// - /// A new one-band image with pixels is - /// created from the array. These images are useful with the libvips - /// convolution operator . - /// - /// Create the image from these values. - /// Default to 1.0. What to divide each pixel by after - /// convolution. Useful for integer convolution masks. - /// Default to 0.0. What to subtract from each pixel - /// after convolution. Useful for integer convolution masks. - /// A new . - /// If unable to make image from . - public static Image NewFromArray(T[,] array, double scale = 1.0, double offset = 0.0) - where T : struct, IEquatable - { - var height = array.GetLength(0); - var width = array.GetLength(1); - var n = width * height; - - var a = new double[n]; - for (var y = 0; y < height; y++) - { - for (var x = 0; x < width; x++) - { - ref var value = ref a[x + (y * width)]; - value = Convert.ToDouble(array[y, x]); - } - } - - var vi = VipsImage.NewMatrixFromArray(width, height, a, n); - if (vi == IntPtr.Zero) - { - throw new VipsException("unable to make image from matrix"); - } + options.Add("string_options", strOptions); - using var image = new Image(vi); - return image.Mutate(mutable => - { - // be careful to set them as double - mutable.Set(GValue.GDoubleType, nameof(scale), scale); - mutable.Set(GValue.GDoubleType, nameof(offset), offset); - }); + if (operationName != null) + { + return Operation.Call(operationName, options, source) as Image; } - /// - /// Create an image from a 2D array. - /// - /// - /// A new one-band image with pixels is - /// created from the array. These images are useful with the libvips - /// convolution operator . - /// - /// Create the image from these values. - /// Default to 1.0. What to divide each pixel by after - /// convolution. Useful for integer convolution masks. - /// Default to 0.0. What to subtract from each pixel - /// after convolution. Useful for integer convolution masks. - /// A new . - /// If unable to make image from . - public static Image NewFromArray(T[][] array, double scale = 1.0, double offset = 0.0) - where T : struct, IEquatable - { - var height = array.Length; - var width = array[0].Length; - var n = width * height; - - var a = new double[n]; - for (var y = 0; y < height; y++) - { - for (var x = 0; x < width; x++) - { - ref var value = ref a[x + (y * width)]; - value = Convert.ToDouble(array[y][x]); - } - } + #region fallback mechanism - var vi = VipsImage.NewMatrixFromArray(width, height, a, n); - if (vi == IntPtr.Zero) + var filename = VipsConnection.FileName(source); + if (filename != IntPtr.Zero) + { + // Try with the old file-based loaders. + operationName = Marshal.PtrToStringAnsi(VipsForeign.FindLoad(filename)); + if (operationName == null) { - throw new VipsException("unable to make image from matrix"); + throw new VipsException("unable to load from source"); } - using var image = new Image(vi); - return image.Mutate(mutable => - { - // be careful to set them as double - mutable.Set(GValue.GDoubleType, nameof(scale), scale); - mutable.Set(GValue.GDoubleType, nameof(offset), offset); - }); + return Operation.Call(operationName, options, filename.ToUtf8String()) as Image; } - /// - /// Create an image from a 1D array. - /// - /// - /// A new one-band image with pixels is - /// created from the array. These images are useful with the libvips - /// convolution operator . - /// - /// Create the image from these values. - /// 1D arrays become a single row of pixels. - /// Default to 1.0. What to divide each pixel by after - /// convolution. Useful for integer convolution masks. - /// Default to 0.0. What to subtract from each pixel - /// after convolution. Useful for integer convolution masks. - /// A new . - /// If unable to make image from . - public static Image NewFromArray(T[] array, double scale = 1.0, double offset = 0.0) - where T : struct, IEquatable - { - var height = array.Length; - var a = new double[height]; - for (var y = 0; y < height; y++) - { - ref var value = ref a[y]; - value = Convert.ToDouble(array[y]); - } + // Try with the old buffer-based loaders. + // TODO: + // Do we need to check if the source can be efficiently mapped into + // memory with `vips_source_is_mappable`? + // This implicitly means that it will not work with network streams + // (`is_pipe` streams). - var vi = VipsImage.NewMatrixFromArray(1, height, a, height); - if (vi == IntPtr.Zero) - { - throw new VipsException("unable to make image from matrix"); - } + var ptr = VipsSource.MapBlob(source); + if (ptr == IntPtr.Zero) + { + throw new VipsException("unable to load from source"); + } - using var image = new Image(vi); - return image.Mutate(mutable => - { - // be careful to set them as double - mutable.Set(GValue.GDoubleType, nameof(scale), scale); - mutable.Set(GValue.GDoubleType, nameof(offset), offset); - }); + using var blob = new VipsBlob(ptr); + var buf = blob.GetData(out var length); + + operationName = Marshal.PtrToStringAnsi(VipsForeign.FindLoadBuffer(buf, length)); + if (operationName == null) + { + throw new VipsException("unable to load from source"); } - /// - /// Wrap an image around a memory array. - /// - /// - /// Wraps an image around a C-style memory array. For example, if the - /// memory array contains four bytes with the - /// values 1, 2, 3, 4, you can make a one-band, 2x2 uchar image from - /// it like this: - /// - /// using var image = Image.NewFromMemory(data, 2, 2, 1, Enums.BandFormat.Uchar); - /// - /// A reference is kept to the data object, so it will not be - /// garbage-collected until the returned image is garbage-collected. - /// - /// This method is useful for efficiently transferring images from GDI+ - /// into libvips. - /// - /// See for the opposite operation. - /// - /// Use to set other image attributes. - /// - /// A memory object. - /// Image width in pixels. - /// Image height in pixels. - /// Number of bands. - /// Band format. - /// A new . - /// If unable to make image from . - public static Image NewFromMemory( - Array data, - int width, - int height, - int bands, - Enums.BandFormat format) - { - var handle = GCHandle.Alloc(data, GCHandleType.Pinned); - var vi = VipsImage.NewFromMemory(handle.AddrOfPinnedObject(), (nuint)data.Length, width, height, bands, - format); - if (vi == IntPtr.Zero) - { - if (handle.IsAllocated) - { - handle.Free(); - } + return Operation.Call(operationName, options, blob) as Image; - throw new VipsException("unable to make image from memory"); - } + #endregion + } - var image = new Image(vi); + /// + /// Load a formatted image from a stream. + /// + /// + /// This behaves exactly as , but the image is + /// loaded from a stream rather than from a source. + /// At least libvips 8.9 is needed. + /// + /// The stream to load the image from. + /// Load options as a string. Use for no options. + /// Hint the expected access pattern for the image. + /// The type of error that will cause load to fail. By + /// default, loaders are permissive, that is, . + /// Optional options that depend on the load operation. + /// A new . + /// If unable to load from . + public static Image NewFromStream( + Stream stream, + string strOptions = "", + Enums.Access? access = null, + Enums.FailOn? failOn = null, + VOption kwargs = null) + { + var source = SourceStream.NewFromStream(stream); + var image = NewFromSource(source, strOptions, access, failOn, kwargs); - // Need to release the pinned GCHandle when the image is closed. - image.OnPostClose += () => - { - if (handle.IsAllocated) - { - handle.Free(); - } - }; + // Need to dispose the SourceStream when the image is closed. + image.OnPostClose += () => source.Dispose(); - return image; - } + return image; + } - /// - /// Wrap an image around a memory area. - /// - /// - /// Because libvips is "borrowing" from the caller, this function - /// is extremely dangerous. Unless you are very careful, you will get crashes or memory - /// corruption. Use instead if you are at all unsure. - /// - /// A unmanaged block of memory. - /// Length of memory. - /// Image width in pixels. - /// Image height in pixels. - /// Number of bands. - /// Band format. - /// A new . - /// If unable to make image from . - public static Image NewFromMemory( - nint data, - ulong size, - int width, - int height, - int bands, - Enums.BandFormat format) - { - var vi = VipsImage.NewFromMemory(data, (nuint)size, width, height, bands, format); - if (vi == IntPtr.Zero) - { - throw new VipsException("unable to make image from memory"); - } - return new Image(vi) { MemoryPressure = (long)size }; - } + /// + /// Create an image from a 2D array. + /// + /// + /// A new one-band image with pixels is + /// created from the array. These images are useful with the libvips + /// convolution operator . + /// + /// Create the image from these values. + /// Default to 1.0. What to divide each pixel by after + /// convolution. Useful for integer convolution masks. + /// Default to 0.0. What to subtract from each pixel + /// after convolution. Useful for integer convolution masks. + /// A new . + /// If unable to make image from . + public static Image NewFromArray(T[,] array, double scale = 1.0, double offset = 0.0) + where T : struct, IEquatable + { + var height = array.GetLength(0); + var width = array.GetLength(1); + var n = width * height; - /// - /// Like , but libvips - /// will make a copy of the memory area. This means more memory use and an extra copy - /// operation, but is much simpler and safer. - /// - /// A unmanaged block of memory. - /// Length of memory. - /// Image width in pixels. - /// Image height in pixels. - /// Number of bands. - /// Band format. - /// A new . - /// If unable to make image from . - public static Image NewFromMemoryCopy( - nint data, - ulong size, - int width, - int height, - int bands, - Enums.BandFormat format) - { - var vi = VipsImage.NewFromMemoryCopy(data, (nuint)size, width, height, bands, format); - if (vi == IntPtr.Zero) + var a = new double[n]; + for (var y = 0; y < height; y++) + { + for (var x = 0; x < width; x++) { - throw new VipsException("unable to make image from memory"); + ref var value = ref a[x + (y * width)]; + value = Convert.ToDouble(array[y, x]); } + } - return new Image(vi) { MemoryPressure = (long)size }; + var vi = VipsImage.NewMatrixFromArray(width, height, a, n); + if (vi == IntPtr.Zero) + { + throw new VipsException("unable to make image from matrix"); } - /// - /// Make a new temporary image. - /// - /// - /// Returns an image backed by a temporary file. When written to with - /// , a temporary file will be created on disc in the - /// specified format. When the image is closed, the file will be deleted - /// automatically. - /// - /// The file is created in the temporary directory. This is set with - /// the environment variable `TMPDIR`. If this is not set, then on - /// Unix systems, vips will default to `/tmp`. On Windows, vips uses - /// `GetTempPath()` to find the temporary directory. - /// - /// vips uses `g_mkstemp()` to make the temporary filename. They - /// generally look something like `vips-12-EJKJFGH.v`. - /// - /// The format for the temp file, for example - /// `%s.v` for a vips format file. The `%s` is - /// substituted by the file path. - /// A new . - /// If unable to make temp file from . - public static Image NewTempFile(string format) - { - var vi = VipsImage.NewTempFile(Encoding.UTF8.GetBytes(format)); - if (vi == IntPtr.Zero) + using var image = new Image(vi); + return image.Mutate(mutable => + { + // be careful to set them as double + mutable.Set(GValue.GDoubleType, nameof(scale), scale); + mutable.Set(GValue.GDoubleType, nameof(offset), offset); + }); + } + + /// + /// Create an image from a 2D array. + /// + /// + /// A new one-band image with pixels is + /// created from the array. These images are useful with the libvips + /// convolution operator . + /// + /// Create the image from these values. + /// Default to 1.0. What to divide each pixel by after + /// convolution. Useful for integer convolution masks. + /// Default to 0.0. What to subtract from each pixel + /// after convolution. Useful for integer convolution masks. + /// A new . + /// If unable to make image from . + public static Image NewFromArray(T[][] array, double scale = 1.0, double offset = 0.0) + where T : struct, IEquatable + { + var height = array.Length; + var width = array[0].Length; + var n = width * height; + + var a = new double[n]; + for (var y = 0; y < height; y++) + { + for (var x = 0; x < width; x++) { - throw new VipsException("unable to make temp file"); + ref var value = ref a[x + (y * width)]; + value = Convert.ToDouble(array[y][x]); } + } - return new Image(vi); + var vi = VipsImage.NewMatrixFromArray(width, height, a, n); + if (vi == IntPtr.Zero) + { + throw new VipsException("unable to make image from matrix"); } - /// - /// Make a new image from an existing one. - /// - /// - /// A new image is created which has the same size, format, interpretation - /// and resolution as `this`, but with every pixel set to `value`. - /// - /// The value for the pixels. Use a - /// single number to make a one-band image; use an array constant - /// to make a many-band image. - /// A new . - public Image NewFromImage(Image value) - { - using var black = Black(1, 1); - using var pixel = black + value; - using var cast = pixel.Cast(Format); - using var image = cast.Embed(0, 0, Width, Height, extend: Enums.Extend.Copy); - return image.Copy(interpretation: Interpretation, xres: Xres, yres: Yres, xoffset: Xoffset, - yoffset: Yoffset); + using var image = new Image(vi); + return image.Mutate(mutable => + { + // be careful to set them as double + mutable.Set(GValue.GDoubleType, nameof(scale), scale); + mutable.Set(GValue.GDoubleType, nameof(offset), offset); + }); + } + + /// + /// Create an image from a 1D array. + /// + /// + /// A new one-band image with pixels is + /// created from the array. These images are useful with the libvips + /// convolution operator . + /// + /// Create the image from these values. + /// 1D arrays become a single row of pixels. + /// Default to 1.0. What to divide each pixel by after + /// convolution. Useful for integer convolution masks. + /// Default to 0.0. What to subtract from each pixel + /// after convolution. Useful for integer convolution masks. + /// A new . + /// If unable to make image from . + public static Image NewFromArray(T[] array, double scale = 1.0, double offset = 0.0) + where T : struct, IEquatable + { + var height = array.Length; + var a = new double[height]; + for (var y = 0; y < height; y++) + { + ref var value = ref a[y]; + value = Convert.ToDouble(array[y]); } - /// - /// Make a new image from an existing one. - /// - /// - /// A new image is created which has the same size, format, interpretation - /// and resolution as `this`, but with every pixel set to `value`. - /// - /// The value for the pixels. Use a - /// single number to make a one-band image; use an array constant - /// to make a many-band image. - /// A new . - public Image NewFromImage(params double[] doubles) - { - using var black = Black(1, 1); - using var pixel = black + doubles; - using var cast = pixel.Cast(Format); - using var image = cast.Embed(0, 0, Width, Height, extend: Enums.Extend.Copy); - return image.Copy(interpretation: Interpretation, xres: Xres, yres: Yres, xoffset: Xoffset, - yoffset: Yoffset); + var vi = VipsImage.NewMatrixFromArray(1, height, a, height); + if (vi == IntPtr.Zero) + { + throw new VipsException("unable to make image from matrix"); } - /// - /// Make a new image from an existing one. - /// - /// - /// A new image is created which has the same size, format, interpretation - /// and resolution as `this`, but with every pixel set to `value`. - /// - /// The value for the pixels. Use a - /// single number to make a one-band image; use an array constant - /// to make a many-band image. - /// A new . - public Image NewFromImage(params int[] ints) => - NewFromImage(Array.ConvertAll(ints, Convert.ToDouble)); - - /// - /// Copy an image to memory. - /// - /// - /// A large area of memory is allocated, the image is rendered to that - /// memory area, and a new image is returned which wraps that large memory - /// area. - /// - /// A new . - /// If unable to copy to memory. - public Image CopyMemory() - { - var vi = VipsImage.CopyMemory(this); - if (vi == IntPtr.Zero) + using var image = new Image(vi); + return image.Mutate(mutable => + { + // be careful to set them as double + mutable.Set(GValue.GDoubleType, nameof(scale), scale); + mutable.Set(GValue.GDoubleType, nameof(offset), offset); + }); + } + + /// + /// Wrap an image around a memory array. + /// + /// + /// Wraps an image around a C-style memory array. For example, if the + /// memory array contains four bytes with the + /// values 1, 2, 3, 4, you can make a one-band, 2x2 uchar image from + /// it like this: + /// + /// using var image = Image.NewFromMemory(data, 2, 2, 1, Enums.BandFormat.Uchar); + /// + /// A reference is kept to the data object, so it will not be + /// garbage-collected until the returned image is garbage-collected. + /// + /// This method is useful for efficiently transferring images from GDI+ + /// into libvips. + /// + /// See for the opposite operation. + /// + /// Use to set other image attributes. + /// + /// A memory object. + /// Image width in pixels. + /// Image height in pixels. + /// Number of bands. + /// Band format. + /// A new . + /// If unable to make image from . + public static Image NewFromMemory( + Array data, + int width, + int height, + int bands, + Enums.BandFormat format) + { + var handle = GCHandle.Alloc(data, GCHandleType.Pinned); + var vi = VipsImage.NewFromMemory(handle.AddrOfPinnedObject(), (nuint)data.Length, width, height, bands, + format); + if (vi == IntPtr.Zero) + { + if (handle.IsAllocated) { - throw new VipsException("unable to copy to memory"); + handle.Free(); } - return new Image(vi) { MemoryPressure = MemoryPressure }; + throw new VipsException("unable to make image from memory"); } - #endregion - - #region writers - - /// - /// Write an image to a file on disc. - /// - /// - /// This method can save images in any format supported by vips. The format - /// is selected from the filename suffix. The filename can include embedded - /// save options, see . - /// - /// For example: - /// - /// image.WriteToFile("fred.jpg[Q=95]"); - /// - /// You can also supply options as keyword arguments, for example: - /// - /// image.WriteToFile("fred.jpg", new VOption - /// { - /// {"Q", 95} - /// }); - /// - /// The full set of options available depend upon the save operation that - /// will be executed. Try something like: - /// - /// $ vips jpegsave - /// - /// at the command-line to see a summary of the available options for the - /// JPEG saver. - /// - /// The disc file to save the image to, with - /// optional appended arguments. - /// Optional options that depend on the save operation. - /// If unable to write to . - public void WriteToFile(string vipsFilename, VOption kwargs = null) - { - var bytes = Encoding.UTF8.GetBytes(vipsFilename + char.MinValue); // Ensure null-terminated string - var filename = Vips.GetFilename(bytes); - var fileOptions = Vips.GetOptions(bytes).ToUtf8String(true); - var operationName = Marshal.PtrToStringAnsi(VipsForeign.FindSave(filename)); + var image = new Image(vi); - if (operationName == null) + // Need to release the pinned GCHandle when the image is closed. + image.OnPostClose += () => + { + if (handle.IsAllocated) { - throw new VipsException($"unable to write to file {vipsFilename}"); + handle.Free(); } + }; - var stringOptions = new VOption - { - {"string_options", fileOptions} - }; - - if (kwargs != null) - { - kwargs.Merge(stringOptions); - } - else - { - kwargs = stringOptions; - } + return image; + } - this.Call(operationName, kwargs, filename.ToUtf8String(true)); + /// + /// Wrap an image around a memory area. + /// + /// + /// Because libvips is "borrowing" from the caller, this function + /// is extremely dangerous. Unless you are very careful, you will get crashes or memory + /// corruption. Use instead if you are at all unsure. + /// + /// A unmanaged block of memory. + /// Length of memory. + /// Image width in pixels. + /// Image height in pixels. + /// Number of bands. + /// Band format. + /// A new . + /// If unable to make image from . + public static Image NewFromMemory( + nint data, + ulong size, + int width, + int height, + int bands, + Enums.BandFormat format) + { + var vi = VipsImage.NewFromMemory(data, (nuint)size, width, height, bands, format); + if (vi == IntPtr.Zero) + { + throw new VipsException("unable to make image from memory"); } - /// - /// Write an image to a formatted string. - /// - /// - /// This method can save images in any format supported by vips. The format - /// is selected from the suffix in the format string. This can include - /// embedded save options, see . - /// - /// For example: - /// - /// var data = image.WriteToBuffer(".jpg[Q=95]"); - /// - /// You can also supply options as keyword arguments, for example: - /// - /// var data = image.WriteToBuffer(".jpg", new VOption - /// { - /// {"Q", 95} - /// }); - /// - /// The full set of options available depend upon the load operation that - /// will be executed. Try something like: - /// - /// $ vips jpegsave_buffer - /// - /// at the command-line to see a summary of the available options for the - /// JPEG saver. - /// - /// The suffix, plus any string-form arguments. - /// Optional options that depend on the save operation. - /// An array of bytes. - /// If unable to write to buffer. - public byte[] WriteToBuffer(string formatString, VOption kwargs = null) - { - var bytes = Encoding.UTF8.GetBytes(formatString + char.MinValue); // Ensure null-terminated string - var bufferOptions = Vips.GetOptions(bytes).ToUtf8String(true); - string operationName = null; - - // Save with the new target API if we can. Fall back to the older - // mechanism in case the saver we need has not been converted yet. - // We need to hide any errors from this first phase. - if (NetVips.AtLeastLibvips(8, 9)) - { - Vips.ErrorFreeze(); - operationName = Marshal.PtrToStringAnsi(VipsForeign.FindSaveTarget(bytes)); - Vips.ErrorThaw(); - } + return new Image(vi) { MemoryPressure = (long)size }; + } - var stringOptions = new VOption - { - {"string_options", bufferOptions} - }; + /// + /// Like , but libvips + /// will make a copy of the memory area. This means more memory use and an extra copy + /// operation, but is much simpler and safer. + /// + /// A unmanaged block of memory. + /// Length of memory. + /// Image width in pixels. + /// Image height in pixels. + /// Number of bands. + /// Band format. + /// A new . + /// If unable to make image from . + public static Image NewFromMemoryCopy( + nint data, + ulong size, + int width, + int height, + int bands, + Enums.BandFormat format) + { + var vi = VipsImage.NewFromMemoryCopy(data, (nuint)size, width, height, bands, format); + if (vi == IntPtr.Zero) + { + throw new VipsException("unable to make image from memory"); + } - if (kwargs != null) - { - kwargs.Merge(stringOptions); - } - else - { - kwargs = stringOptions; - } + return new Image(vi) { MemoryPressure = (long)size }; + } - if (operationName != null) - { - using var target = Target.NewToMemory(); - this.Call(operationName, kwargs, target); - return target.Blob; - } + /// + /// Make a new temporary image. + /// + /// + /// Returns an image backed by a temporary file. When written to with + /// , a temporary file will be created on disc in the + /// specified format. When the image is closed, the file will be deleted + /// automatically. + /// + /// The file is created in the temporary directory. This is set with + /// the environment variable `TMPDIR`. If this is not set, then on + /// Unix systems, vips will default to `/tmp`. On Windows, vips uses + /// `GetTempPath()` to find the temporary directory. + /// + /// vips uses `g_mkstemp()` to make the temporary filename. They + /// generally look something like `vips-12-EJKJFGH.v`. + /// + /// The format for the temp file, for example + /// `%s.v` for a vips format file. The `%s` is + /// substituted by the file path. + /// A new . + /// If unable to make temp file from . + public static Image NewTempFile(string format) + { + var vi = VipsImage.NewTempFile(Encoding.UTF8.GetBytes(format)); + if (vi == IntPtr.Zero) + { + throw new VipsException("unable to make temp file"); + } - #region fallback mechanism + return new Image(vi); + } - operationName = Marshal.PtrToStringAnsi(VipsForeign.FindSaveBuffer(bytes)); - if (operationName == null) - { - throw new VipsException($"unable to write to buffer"); - } + /// + /// Make a new image from an existing one. + /// + /// + /// A new image is created which has the same size, format, interpretation + /// and resolution as `this`, but with every pixel set to `value`. + /// + /// The value for the pixels. Use a + /// single number to make a one-band image; use an array constant + /// to make a many-band image. + /// A new . + public Image NewFromImage(Image value) + { + using var black = Black(1, 1); + using var pixel = black + value; + using var cast = pixel.Cast(Format); + using var image = cast.Embed(0, 0, Width, Height, extend: Enums.Extend.Copy); + return image.Copy(interpretation: Interpretation, xres: Xres, yres: Yres, xoffset: Xoffset, + yoffset: Yoffset); + } - return this.Call(operationName, kwargs) as byte[]; + /// + /// Make a new image from an existing one. + /// + /// + /// A new image is created which has the same size, format, interpretation + /// and resolution as `this`, but with every pixel set to `value`. + /// + /// The value for the pixels. Use a + /// single number to make a one-band image; use an array constant + /// to make a many-band image. + /// A new . + public Image NewFromImage(params double[] doubles) + { + using var black = Black(1, 1); + using var pixel = black + doubles; + using var cast = pixel.Cast(Format); + using var image = cast.Embed(0, 0, Width, Height, extend: Enums.Extend.Copy); + return image.Copy(interpretation: Interpretation, xres: Xres, yres: Yres, xoffset: Xoffset, + yoffset: Yoffset); + } - #endregion - } + /// + /// Make a new image from an existing one. + /// + /// + /// A new image is created which has the same size, format, interpretation + /// and resolution as `this`, but with every pixel set to `value`. + /// + /// The value for the pixels. Use a + /// single number to make a one-band image; use an array constant + /// to make a many-band image. + /// A new . + public Image NewFromImage(params int[] ints) => + NewFromImage(Array.ConvertAll(ints, Convert.ToDouble)); - /// - /// Write an image to a target. - /// - /// - /// This behaves exactly as , but the image is - /// written to a target rather than a file. - /// At least libvips 8.9 is needed. - /// - /// Write to this target. - /// The suffix, plus any string-form arguments. - /// Optional options that depend on the save operation. - /// If unable to write to target. - public void WriteToTarget(Target target, string formatString, VOption kwargs = null) - { - var bytes = Encoding.UTF8.GetBytes(formatString + char.MinValue); // Ensure null-terminated string - var bufferOptions = Vips.GetOptions(bytes).ToUtf8String(true); - var operationName = Marshal.PtrToStringAnsi(VipsForeign.FindSaveTarget(bytes)); + /// + /// Copy an image to memory. + /// + /// + /// A large area of memory is allocated, the image is rendered to that + /// memory area, and a new image is returned which wraps that large memory + /// area. + /// + /// A new . + /// If unable to copy to memory. + public Image CopyMemory() + { + var vi = VipsImage.CopyMemory(this); + if (vi == IntPtr.Zero) + { + throw new VipsException("unable to copy to memory"); + } - if (operationName == null) - { - throw new VipsException("unable to write to target"); - } + return new Image(vi) { MemoryPressure = MemoryPressure }; + } - var stringOptions = new VOption - { - {"string_options", bufferOptions} - }; + #endregion - if (kwargs != null) - { - kwargs.Merge(stringOptions); - } - else - { - kwargs = stringOptions; - } + #region writers - this.Call(operationName, kwargs, target); - } + /// + /// Write an image to a file on disc. + /// + /// + /// This method can save images in any format supported by vips. The format + /// is selected from the filename suffix. The filename can include embedded + /// save options, see . + /// + /// For example: + /// + /// image.WriteToFile("fred.jpg[Q=95]"); + /// + /// You can also supply options as keyword arguments, for example: + /// + /// image.WriteToFile("fred.jpg", new VOption + /// { + /// {"Q", 95} + /// }); + /// + /// The full set of options available depend upon the save operation that + /// will be executed. Try something like: + /// + /// $ vips jpegsave + /// + /// at the command-line to see a summary of the available options for the + /// JPEG saver. + /// + /// The disc file to save the image to, with + /// optional appended arguments. + /// Optional options that depend on the save operation. + /// If unable to write to . + public void WriteToFile(string vipsFilename, VOption kwargs = null) + { + var bytes = Encoding.UTF8.GetBytes(vipsFilename + char.MinValue); // Ensure null-terminated string + var filename = Vips.GetFilename(bytes); + var fileOptions = Vips.GetOptions(bytes).ToUtf8String(true); + var operationName = Marshal.PtrToStringAnsi(VipsForeign.FindSave(filename)); - /// - /// Write an image to a stream. - /// - /// - /// This behaves exactly as , but the image is - /// written to a stream rather than a target. - /// At least libvips 8.9 is needed. - /// - /// Write to this stream. - /// The suffix, plus any string-form arguments. - /// Optional options that depend on the save operation. - /// If unable to write to stream. - public void WriteToStream(Stream stream, string formatString, VOption kwargs = null) - { - using var target = TargetStream.NewFromStream(stream); - WriteToTarget(target, formatString, kwargs); + if (operationName == null) + { + throw new VipsException($"unable to write to file {vipsFilename}"); } + var stringOptions = new VOption + { + {"string_options", fileOptions} + }; - /// - /// Write the image to memory as a simple, unformatted C-style array. - /// - /// - /// The caller is responsible for freeing this memory with . - /// - /// Output buffer length. - /// A pointing to an unformatted C-style array. - /// If unable to write to memory. - public nint WriteToMemory(out ulong size) + if (kwargs != null) { - var pointer = VipsImage.WriteToMemory(this, out size); - if (pointer == IntPtr.Zero) - { - throw new VipsException("unable to write to memory"); - } + kwargs.Merge(stringOptions); + } + else + { + kwargs = stringOptions; + } - return pointer; + this.Call(operationName, kwargs, filename.ToUtf8String(true)); + } + + /// + /// Write an image to a formatted string. + /// + /// + /// This method can save images in any format supported by vips. The format + /// is selected from the suffix in the format string. This can include + /// embedded save options, see . + /// + /// For example: + /// + /// var data = image.WriteToBuffer(".jpg[Q=95]"); + /// + /// You can also supply options as keyword arguments, for example: + /// + /// var data = image.WriteToBuffer(".jpg", new VOption + /// { + /// {"Q", 95} + /// }); + /// + /// The full set of options available depend upon the load operation that + /// will be executed. Try something like: + /// + /// $ vips jpegsave_buffer + /// + /// at the command-line to see a summary of the available options for the + /// JPEG saver. + /// + /// The suffix, plus any string-form arguments. + /// Optional options that depend on the save operation. + /// An array of bytes. + /// If unable to write to buffer. + public byte[] WriteToBuffer(string formatString, VOption kwargs = null) + { + var bytes = Encoding.UTF8.GetBytes(formatString + char.MinValue); // Ensure null-terminated string + var bufferOptions = Vips.GetOptions(bytes).ToUtf8String(true); + string operationName = null; + + // Save with the new target API if we can. Fall back to the older + // mechanism in case the saver we need has not been converted yet. + // We need to hide any errors from this first phase. + if (NetVips.AtLeastLibvips(8, 9)) + { + Vips.ErrorFreeze(); + operationName = Marshal.PtrToStringAnsi(VipsForeign.FindSaveTarget(bytes)); + Vips.ErrorThaw(); } - /// - /// Write the image to a large memory array. - /// - /// - /// A large area of memory is allocated, the image is rendered to that - /// memory array, and the array is returned as a buffer. - /// - /// For example, if you have a 2x2 uchar image containing the bytes 1, 2, - /// 3, 4, read left-to-right, top-to-bottom, then: - /// - /// var buf = image.WriteToMemory(); - /// - /// will return a four byte buffer containing the values 1, 2, 3, 4. - /// - /// An array of bytes. - /// If unable to write to memory. - public byte[] WriteToMemory() - { - var pointer = WriteToMemory(out var size); - - var managedArray = new byte[size]; - Marshal.Copy(pointer, managedArray, 0, (int)size); - - GLib.GFree(pointer); - - return managedArray; + var stringOptions = new VOption + { + {"string_options", bufferOptions} + }; + + if (kwargs != null) + { + kwargs.Merge(stringOptions); + } + else + { + kwargs = stringOptions; } - /// - /// Write an image to another image. - /// - /// - /// This function writes `this` to another image. Use something like - /// to make an image that can be written to. - /// - /// The to write to. - /// If unable to write to image. - public void Write(Image other) - { - var result = VipsImage.Write(this, other); - if (result != 0) - { - throw new VipsException("unable to write to image"); - } + if (operationName != null) + { + using var target = Target.NewToMemory(); + this.Call(operationName, kwargs, target); + return target.Blob; } + #region fallback mechanism + + operationName = Marshal.PtrToStringAnsi(VipsForeign.FindSaveBuffer(bytes)); + if (operationName == null) + { + throw new VipsException($"unable to write to buffer"); + } + + return this.Call(operationName, kwargs) as byte[]; + #endregion + } - #region get metadata - - /// - /// Get the GType of an item of metadata. - /// - /// - /// Fetch the GType of a piece of metadata, or if the named - /// item does not exist. See . - /// - /// The name of the piece of metadata to get the type of. - /// A new instance of initialized to the GType or - /// if the property does not exist. - public new nint GetTypeOf(string name) - { - // on libvips before 8.5, property types must be fetched separately, - // since built-in enums were reported as ints - if (!NetVips.AtLeastLibvips(8, 5)) - { - var gtype = base.GetTypeOf(name); - if (gtype != IntPtr.Zero) - { - return gtype; - } - } + /// + /// Write an image to a target. + /// + /// + /// This behaves exactly as , but the image is + /// written to a target rather than a file. + /// At least libvips 8.9 is needed. + /// + /// Write to this target. + /// The suffix, plus any string-form arguments. + /// Optional options that depend on the save operation. + /// If unable to write to target. + public void WriteToTarget(Target target, string formatString, VOption kwargs = null) + { + var bytes = Encoding.UTF8.GetBytes(formatString + char.MinValue); // Ensure null-terminated string + var bufferOptions = Vips.GetOptions(bytes).ToUtf8String(true); + var operationName = Marshal.PtrToStringAnsi(VipsForeign.FindSaveTarget(bytes)); - return VipsImage.GetTypeof(this, name); + if (operationName == null) + { + throw new VipsException("unable to write to target"); } - /// - /// Check if the underlying image contains an property of metadata. - /// - /// The name of the piece of metadata to check for. - /// if the metadata exits; otherwise, . - public bool Contains(string name) + var stringOptions = new VOption + { + {"string_options", bufferOptions} + }; + + if (kwargs != null) + { + kwargs.Merge(stringOptions); + } + else { - return GetTypeOf(name) != IntPtr.Zero; + kwargs = stringOptions; } - /// - /// Get an item of metadata. - /// - /// - /// Fetches an item of metadata as a C# value. For example: - /// - /// var orientation = image.Get("orientation"); - /// - /// would fetch the image orientation. - /// - /// The name of the piece of metadata to get. - /// The metadata item as a C# value. - /// If unable to get . - public new object Get(string name) - { - switch (name) - { - // scale and offset have default values - case "scale" when !Contains("scale"): - return 1.0; - case "offset" when !Contains("offset"): - return 0.0; - } + this.Call(operationName, kwargs, target); + } - // with old libvips, we must fetch properties (as opposed to - // metadata) via VipsObject - if (!NetVips.AtLeastLibvips(8, 5)) - { - var gtype = base.GetTypeOf(name); - if (gtype != IntPtr.Zero) - { - return base.Get(name); - } - } + /// + /// Write an image to a stream. + /// + /// + /// This behaves exactly as , but the image is + /// written to a stream rather than a target. + /// At least libvips 8.9 is needed. + /// + /// Write to this stream. + /// The suffix, plus any string-form arguments. + /// Optional options that depend on the save operation. + /// If unable to write to stream. + public void WriteToStream(Stream stream, string formatString, VOption kwargs = null) + { + using var target = TargetStream.NewFromStream(stream); + WriteToTarget(target, formatString, kwargs); + } - var result = VipsImage.Get(this, name, out var gvCopy); - if (result != 0) - { - throw new VipsException($"unable to get {name}"); - } - using var gv = new GValue(gvCopy); - return gv.Get(); + /// + /// Write the image to memory as a simple, unformatted C-style array. + /// + /// + /// The caller is responsible for freeing this memory with . + /// + /// Output buffer length. + /// A pointing to an unformatted C-style array. + /// If unable to write to memory. + public nint WriteToMemory(out ulong size) + { + var pointer = VipsImage.WriteToMemory(this, out size); + if (pointer == IntPtr.Zero) + { + throw new VipsException("unable to write to memory"); + } + + return pointer; + } + + /// + /// Write the image to a large memory array. + /// + /// + /// A large area of memory is allocated, the image is rendered to that + /// memory array, and the array is returned as a buffer. + /// + /// For example, if you have a 2x2 uchar image containing the bytes 1, 2, + /// 3, 4, read left-to-right, top-to-bottom, then: + /// + /// var buf = image.WriteToMemory(); + /// + /// will return a four byte buffer containing the values 1, 2, 3, 4. + /// + /// An array of bytes. + /// If unable to write to memory. + public byte[] WriteToMemory() + { + var pointer = WriteToMemory(out var size); + + var managedArray = new byte[size]; + Marshal.Copy(pointer, managedArray, 0, (int)size); + + GLib.GFree(pointer); + + return managedArray; + } + + /// + /// Write an image to another image. + /// + /// + /// This function writes `this` to another image. Use something like + /// to make an image that can be written to. + /// + /// The to write to. + /// If unable to write to image. + public void Write(Image other) + { + var result = VipsImage.Write(this, other); + if (result != 0) + { + throw new VipsException("unable to write to image"); } + } + + #endregion - /// - /// Get a list of all the metadata fields on an image. - /// - /// - /// At least libvips 8.5 is needed. - /// - /// An array of strings or . - public string[] GetFields() + #region get metadata + + /// + /// Get the GType of an item of metadata. + /// + /// + /// Fetch the GType of a piece of metadata, or if the named + /// item does not exist. See . + /// + /// The name of the piece of metadata to get the type of. + /// A new instance of initialized to the GType or + /// if the property does not exist. + public new nint GetTypeOf(string name) + { + // on libvips before 8.5, property types must be fetched separately, + // since built-in enums were reported as ints + if (!NetVips.AtLeastLibvips(8, 5)) { - if (!NetVips.AtLeastLibvips(8, 5)) + var gtype = base.GetTypeOf(name); + if (gtype != IntPtr.Zero) { - return null; + return gtype; } + } - var ptrArr = VipsImage.GetFields(this); + return VipsImage.GetTypeof(this, name); + } - var names = new List(); + /// + /// Check if the underlying image contains an property of metadata. + /// + /// The name of the piece of metadata to check for. + /// if the metadata exits; otherwise, . + public bool Contains(string name) + { + return GetTypeOf(name) != IntPtr.Zero; + } - var count = 0; - nint strPtr; - while ((strPtr = Marshal.ReadIntPtr(ptrArr, count * IntPtr.Size)) != IntPtr.Zero) + /// + /// Get an item of metadata. + /// + /// + /// Fetches an item of metadata as a C# value. For example: + /// + /// var orientation = image.Get("orientation"); + /// + /// would fetch the image orientation. + /// + /// The name of the piece of metadata to get. + /// The metadata item as a C# value. + /// If unable to get . + public new object Get(string name) + { + switch (name) + { + // scale and offset have default values + case "scale" when !Contains("scale"): + return 1.0; + case "offset" when !Contains("offset"): + return 0.0; + } + + // with old libvips, we must fetch properties (as opposed to + // metadata) via VipsObject + if (!NetVips.AtLeastLibvips(8, 5)) + { + var gtype = base.GetTypeOf(name); + if (gtype != IntPtr.Zero) { - var name = Marshal.PtrToStringAnsi(strPtr); - names.Add(name); - GLib.GFree(strPtr); - ++count; + return base.Get(name); } - - GLib.GFree(ptrArr); - - return names.ToArray(); } - /// - /// Returns a string that represents the current image. - /// - /// A string that represents the current image. - public override string ToString() + var result = VipsImage.Get(this, name, out var gvCopy); + if (result != 0) { - return $""; + throw new VipsException($"unable to get {name}"); } - #endregion + using var gv = new GValue(gvCopy); + return gv.Get(); + } - #region handwritten functions - - /// - /// Mutate an image with a delegate. Inside the delegate, you can call methods - /// which modify the image, such as setting or removing metadata, or - /// modifying pixels. - /// - /// - /// - /// using var mutated = image.Mutate(mutable => - /// { - /// for (var i = 0; i <= 100; i++) - /// { - /// var j = i / 100.0; - /// mutable.DrawLine(new[] { 255.0 }, (int)(mutable.Width * j), 0, 0, (int)(mutable.Height * (1 - j))); - /// } - /// }); - /// - /// - /// A new . - public virtual Image Mutate(Action action) - { - // We take a copy of the regular Image to ensure we have an unshared (unique) object. - using var mutable = new MutableImage(Copy()); - action.Invoke(mutable); - return mutable.Image; + /// + /// Get a list of all the metadata fields on an image. + /// + /// + /// At least libvips 8.5 is needed. + /// + /// An array of strings or . + public string[] GetFields() + { + if (!NetVips.AtLeastLibvips(8, 5)) + { + return null; } - /// - /// Scale an image to 0 - 255. - /// - /// - /// This is the libvips `scale` operation, renamed to avoid a clash with - /// the `scale` for convolution masks. - /// - /// - /// - /// using Image @out = in.Scale(exp: double, log: bool); - /// - /// - /// Exponent for log scale. - /// Log scale. - /// A new . - public Image ScaleImage(double? exp = null, bool? log = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(exp), exp); - options.AddIfPresent(nameof(log), log); - - return this.Call("scale", options) as Image; + var ptrArr = VipsImage.GetFields(this); + + var names = new List(); + + var count = 0; + nint strPtr; + while ((strPtr = Marshal.ReadIntPtr(ptrArr, count * IntPtr.Size)) != IntPtr.Zero) + { + var name = Marshal.PtrToStringAnsi(strPtr); + names.Add(name); + GLib.GFree(strPtr); + ++count; } - /// - /// Ifthenelse an image. - /// - /// - /// - /// using Image @out = cond.Ifthenelse(in1, in2, blend: bool); - /// - /// - /// Source for TRUE pixels. - /// Source for FALSE pixels. - /// Blend smoothly between then and else parts. - /// A new . - public Image Ifthenelse(object in1, object in2, bool? blend = null) - { - Image matchImage; - if (in1 is Image th) - { - matchImage = th; - } - else if (in2 is Image el) - { - matchImage = el; - } - else - { - matchImage = this; - } + GLib.GFree(ptrArr); - using var im1 = in1 is Image ? null : Imageize(matchImage, in1); - using var im2 = in2 is Image ? null : Imageize(matchImage, in2); + return names.ToArray(); + } - var options = new VOption(); + /// + /// Returns a string that represents the current image. + /// + /// A string that represents the current image. + public override string ToString() + { + return $""; + } - options.AddIfPresent(nameof(blend), blend); + #endregion - return this.Call("ifthenelse", options, im1 ?? in1, im2 ?? in2) as Image; - } + #region handwritten functions - /// - /// Use pixel values to pick cases from an array of constants. - /// - /// - /// - /// using Image @out = index.Case(10.5, 20.5); - /// - /// - /// Array of constants. - /// A new . - public Image Case(params double[] doubles) => - this.Call("case", doubles) as Image; - - /// - /// Use pixel values to pick cases from an array of constants. - /// - /// - /// - /// using Image @out = index.Case(10, 20); - /// - /// - /// Array of constants. - /// A new . - public Image Case(params int[] ints) => - Case(Array.ConvertAll(ints, Convert.ToDouble)); - - /// - /// Use pixel values to pick cases from an array of images. - /// - /// - /// - /// using Image @out = index.Case(images); - /// - /// - /// Array of case images. - /// A new . - public Image Case(params Image[] images) => - this.Call("case", new object[] { images }) as Image; - - /// - /// Use pixel values to pick cases from an a set of mixed images and constants. - /// - /// - /// - /// using Image @out = index.Case(image, 10); - /// - /// - /// Array of mixed images and constants. - /// A new . - public Image Case(params object[] objects) => - this.Call("case", new object[] { objects }) as Image; - - /// - /// Append a set of constants bandwise. - /// - /// - /// - /// using Image @out = image.Bandjoin(127.5, 255.0); - /// - /// - /// Array of constants. - /// A new . - public Image Bandjoin(params double[] doubles) => - BandjoinConst(doubles); - - /// - /// Append a set of constants bandwise. - /// - /// - /// - /// using Image @out = image.Bandjoin(255, 128); - /// - /// - /// Array of constants. - /// A new . - public Image Bandjoin(params int[] ints) => - BandjoinConst(Array.ConvertAll(ints, Convert.ToDouble)); - - /// - /// Append a set of images bandwise. - /// - /// - /// - /// using Image @out = image.Bandjoin(image2, image3); - /// - /// - /// Array of images. - /// A new . - public Image Bandjoin(params Image[] images) => - this.Call("bandjoin", new object[] { images.PrependImage(this) }) as Image; - - /// - /// Append a set of mixed images and constants bandwise. - /// - /// - /// - /// using Image @out = image.Bandjoin(image2, 255); - /// - /// - /// Array of mixed images and constants. - /// A new . - public Image Bandjoin(params object[] objects) => - this.Call("bandjoin", new object[] { objects.PrependImage(this) }) as Image; - - /// - /// Band-wise rank a set of constants. - /// - /// - /// - /// using Image @out = image.Bandrank(other, index: int); - /// - /// - /// Array of constants. - /// Select this band element from sorted list. - /// A new - public Image Bandrank(double[] doubles, int? index = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(index), index); - - return this.Call("bandrank", options, doubles) as Image; - } + /// + /// Mutate an image with a delegate. Inside the delegate, you can call methods + /// which modify the image, such as setting or removing metadata, or + /// modifying pixels. + /// + /// + /// + /// using var mutated = image.Mutate(mutable => + /// { + /// for (var i = 0; i <= 100; i++) + /// { + /// var j = i / 100.0; + /// mutable.DrawLine(new[] { 255.0 }, (int)(mutable.Width * j), 0, 0, (int)(mutable.Height * (1 - j))); + /// } + /// }); + /// + /// + /// A new . + public virtual Image Mutate(Action action) + { + // We take a copy of the regular Image to ensure we have an unshared (unique) object. + using var mutable = new MutableImage(Copy()); + action.Invoke(mutable); + return mutable.Image; + } - /// - /// Band-wise rank a set of constants. - /// - /// - /// - /// using Image @out = image.Bandrank(other, index: int); - /// - /// - /// Array of constants. - /// Select this band element from sorted list. - /// A new . - public Image Bandrank(int[] ints, int? index = null) => - Bandrank(Array.ConvertAll(ints, Convert.ToDouble), index); - - /// - /// Band-wise rank a set of images. - /// - /// - /// - /// using Image @out = image.Bandrank(other, index: int); - /// - /// - /// Array of input images. - /// Select this band element from sorted list. - /// A new . - public Image Bandrank(Image[] images, int? index = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(index), index); - - return this.Call("bandrank", options, new object[] { images.PrependImage(this) }) as Image; - } + /// + /// Scale an image to 0 - 255. + /// + /// + /// This is the libvips `scale` operation, renamed to avoid a clash with + /// the `scale` for convolution masks. + /// + /// + /// + /// using Image @out = in.Scale(exp: double, log: bool); + /// + /// + /// Exponent for log scale. + /// Log scale. + /// A new . + public Image ScaleImage(double? exp = null, bool? log = null) + { + var options = new VOption(); - /// - /// Band-wise rank a image. - /// - /// - /// - /// using Image @out = image.Bandrank(other, index: int); - /// - /// - /// Input image. - /// Select this band element from sorted list. - /// A new . - public Image Bandrank(Image other, int? index = null) => - Bandrank(new[] { other }, index); - - /// - /// Band-wise rank a set of mixed images and constants. - /// - /// - /// - /// using Image @out = image.Bandrank(new object[] { image2, 255 }, index: int); - /// - /// - /// Array of mixed images and constants. - /// Select this band element from sorted list. - /// A new . - public Image Bandrank(object[] objects, int? index = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(index), index); - - return this.Call("bandrank", options, new object[] { objects.PrependImage(this) }) as Image; - } + options.AddIfPresent(nameof(exp), exp); + options.AddIfPresent(nameof(log), log); - /// - /// Blend an array of images with an array of blend modes. - /// - /// - /// - /// using Image @out = image.Composite(images, modes, x: int[], y: int[], compositingSpace: Enums.Interpretation, premultiplied: bool); - /// - /// - /// Array of input images. - /// Array of VipsBlendMode to join with. - /// Array of x coordinates to join at. - /// Array of y coordinates to join at. - /// Composite images in this colour space. - /// Images have premultiplied alpha. - /// A new . - public Image Composite(Image[] images, Enums.BlendMode[] modes, int[] x = null, int[] y = null, - Enums.Interpretation? compositingSpace = null, bool? premultiplied = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(x), x); - options.AddIfPresent(nameof(y), y); - options.AddIfPresent("compositing_space", compositingSpace); - options.AddIfPresent(nameof(premultiplied), premultiplied); - - return this.Call("composite", options, images.PrependImage(this), modes) as Image; - } + return this.Call("scale", options) as Image; + } - /// - /// A synonym for . - /// - /// - /// - /// using Image @out = base.Composite(overlay, mode, x: int, y: int, compositingSpace: Enums.Interpretation, premultiplied: bool); - /// - /// - /// Overlay image. - /// VipsBlendMode to join with. - /// x position of overlay. - /// y position of overlay. - /// Composite images in this colour space. - /// Images have premultiplied alpha. - /// A new . - public Image Composite(Image overlay, Enums.BlendMode mode, int? x = null, int? y = null, - Enums.Interpretation? compositingSpace = null, bool? premultiplied = null) => - Composite2(overlay, mode, x, y, compositingSpace, premultiplied); - - /// - /// A synonym for . - /// - /// - /// - /// using Image @out = input.Crop(left, top, width, height); - /// - /// - /// Left edge of extract area. - /// Top edge of extract area. - /// Width of extract area. - /// Height of extract area. - /// A new . - public Image Crop(int left, int top, int width, int height) => - ExtractArea(left, top, width, height); - - /// - /// Return the coordinates of the image maximum. - /// - /// An array of doubles. - public double[] MaxPos() - { - var v = Max(out var x, out var y); - return new[] { v, x, y }; + /// + /// Ifthenelse an image. + /// + /// + /// + /// using Image @out = cond.Ifthenelse(in1, in2, blend: bool); + /// + /// + /// Source for TRUE pixels. + /// Source for FALSE pixels. + /// Blend smoothly between then and else parts. + /// A new . + public Image Ifthenelse(object in1, object in2, bool? blend = null) + { + Image matchImage; + if (in1 is Image th) + { + matchImage = th; } + else if (in2 is Image el) + { + matchImage = el; + } + else + { + matchImage = this; + } + + using var im1 = in1 is Image ? null : Imageize(matchImage, in1); + using var im2 = in2 is Image ? null : Imageize(matchImage, in2); + + var options = new VOption(); + + options.AddIfPresent(nameof(blend), blend); + + return this.Call("ifthenelse", options, im1 ?? in1, im2 ?? in2) as Image; + } + + /// + /// Use pixel values to pick cases from an array of constants. + /// + /// + /// + /// using Image @out = index.Case(10.5, 20.5); + /// + /// + /// Array of constants. + /// A new . + public Image Case(params double[] doubles) => + this.Call("case", doubles) as Image; + + /// + /// Use pixel values to pick cases from an array of constants. + /// + /// + /// + /// using Image @out = index.Case(10, 20); + /// + /// + /// Array of constants. + /// A new . + public Image Case(params int[] ints) => + Case(Array.ConvertAll(ints, Convert.ToDouble)); + + /// + /// Use pixel values to pick cases from an array of images. + /// + /// + /// + /// using Image @out = index.Case(images); + /// + /// + /// Array of case images. + /// A new . + public Image Case(params Image[] images) => + this.Call("case", new object[] { images }) as Image; + + /// + /// Use pixel values to pick cases from an a set of mixed images and constants. + /// + /// + /// + /// using Image @out = index.Case(image, 10); + /// + /// + /// Array of mixed images and constants. + /// A new . + public Image Case(params object[] objects) => + this.Call("case", new object[] { objects }) as Image; + + /// + /// Append a set of constants bandwise. + /// + /// + /// + /// using Image @out = image.Bandjoin(127.5, 255.0); + /// + /// + /// Array of constants. + /// A new . + public Image Bandjoin(params double[] doubles) => + BandjoinConst(doubles); + + /// + /// Append a set of constants bandwise. + /// + /// + /// + /// using Image @out = image.Bandjoin(255, 128); + /// + /// + /// Array of constants. + /// A new . + public Image Bandjoin(params int[] ints) => + BandjoinConst(Array.ConvertAll(ints, Convert.ToDouble)); + + /// + /// Append a set of images bandwise. + /// + /// + /// + /// using Image @out = image.Bandjoin(image2, image3); + /// + /// + /// Array of images. + /// A new . + public Image Bandjoin(params Image[] images) => + this.Call("bandjoin", new object[] { images.PrependImage(this) }) as Image; + + /// + /// Append a set of mixed images and constants bandwise. + /// + /// + /// + /// using Image @out = image.Bandjoin(image2, 255); + /// + /// + /// Array of mixed images and constants. + /// A new . + public Image Bandjoin(params object[] objects) => + this.Call("bandjoin", new object[] { objects.PrependImage(this) }) as Image; + + /// + /// Band-wise rank a set of constants. + /// + /// + /// + /// using Image @out = image.Bandrank(other, index: int); + /// + /// + /// Array of constants. + /// Select this band element from sorted list. + /// A new + public Image Bandrank(double[] doubles, int? index = null) + { + var options = new VOption(); - /// - /// Return the coordinates of the image minimum. - /// - /// An array of doubles. - public double[] MinPos() + options.AddIfPresent(nameof(index), index); + + return this.Call("bandrank", options, doubles) as Image; + } + + /// + /// Band-wise rank a set of constants. + /// + /// + /// + /// using Image @out = image.Bandrank(other, index: int); + /// + /// + /// Array of constants. + /// Select this band element from sorted list. + /// A new . + public Image Bandrank(int[] ints, int? index = null) => + Bandrank(Array.ConvertAll(ints, Convert.ToDouble), index); + + /// + /// Band-wise rank a set of images. + /// + /// + /// + /// using Image @out = image.Bandrank(other, index: int); + /// + /// + /// Array of input images. + /// Select this band element from sorted list. + /// A new . + public Image Bandrank(Image[] images, int? index = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(index), index); + + return this.Call("bandrank", options, new object[] { images.PrependImage(this) }) as Image; + } + + /// + /// Band-wise rank a image. + /// + /// + /// + /// using Image @out = image.Bandrank(other, index: int); + /// + /// + /// Input image. + /// Select this band element from sorted list. + /// A new . + public Image Bandrank(Image other, int? index = null) => + Bandrank(new[] { other }, index); + + /// + /// Band-wise rank a set of mixed images and constants. + /// + /// + /// + /// using Image @out = image.Bandrank(new object[] { image2, 255 }, index: int); + /// + /// + /// Array of mixed images and constants. + /// Select this band element from sorted list. + /// A new . + public Image Bandrank(object[] objects, int? index = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(index), index); + + return this.Call("bandrank", options, new object[] { objects.PrependImage(this) }) as Image; + } + + /// + /// Blend an array of images with an array of blend modes. + /// + /// + /// + /// using Image @out = image.Composite(images, modes, x: int[], y: int[], compositingSpace: Enums.Interpretation, premultiplied: bool); + /// + /// + /// Array of input images. + /// Array of VipsBlendMode to join with. + /// Array of x coordinates to join at. + /// Array of y coordinates to join at. + /// Composite images in this colour space. + /// Images have premultiplied alpha. + /// A new . + public Image Composite(Image[] images, Enums.BlendMode[] modes, int[] x = null, int[] y = null, + Enums.Interpretation? compositingSpace = null, bool? premultiplied = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(x), x); + options.AddIfPresent(nameof(y), y); + options.AddIfPresent("compositing_space", compositingSpace); + options.AddIfPresent(nameof(premultiplied), premultiplied); + + return this.Call("composite", options, images.PrependImage(this), modes) as Image; + } + + /// + /// A synonym for . + /// + /// + /// + /// using Image @out = base.Composite(overlay, mode, x: int, y: int, compositingSpace: Enums.Interpretation, premultiplied: bool); + /// + /// + /// Overlay image. + /// VipsBlendMode to join with. + /// x position of overlay. + /// y position of overlay. + /// Composite images in this colour space. + /// Images have premultiplied alpha. + /// A new . + public Image Composite(Image overlay, Enums.BlendMode mode, int? x = null, int? y = null, + Enums.Interpretation? compositingSpace = null, bool? premultiplied = null) => + Composite2(overlay, mode, x, y, compositingSpace, premultiplied); + + /// + /// A synonym for . + /// + /// + /// + /// using Image @out = input.Crop(left, top, width, height); + /// + /// + /// Left edge of extract area. + /// Top edge of extract area. + /// Width of extract area. + /// Height of extract area. + /// A new . + public Image Crop(int left, int top, int width, int height) => + ExtractArea(left, top, width, height); + + /// + /// Return the coordinates of the image maximum. + /// + /// An array of doubles. + public double[] MaxPos() + { + var v = Max(out var x, out var y); + return new[] { v, x, y }; + } + + /// + /// Return the coordinates of the image minimum. + /// + /// An array of doubles. + public double[] MinPos() + { + var v = Min(out var x, out var y); + return new[] { v, x, y }; + } + + /// + /// Return the real part of a complex image. + /// + /// A new . + public Image Real() => Complexget(Enums.OperationComplexget.Real); + + /// + /// Return the imaginary part of a complex image. + /// + /// A new . + public Image Imag() => Complexget(Enums.OperationComplexget.Imag); + + /// + /// Return an image converted to polar coordinates. + /// + /// A new . + public Image Polar() => RunCmplx(x => x.Complex(Enums.OperationComplex.Polar), this); + + /// + /// Return an image converted to rectangular coordinates. + /// + /// A new . + public Image Rect() => RunCmplx(x => x.Complex(Enums.OperationComplex.Rect), this); + + /// + /// Return the complex conjugate of an image. + /// + /// A new . + public Image Conj() => Complex(Enums.OperationComplex.Conj); + + /// + /// Return the sine of an image in degrees. + /// + /// A new . + public Image Sin() => Math(Enums.OperationMath.Sin); + + /// + /// Return the cosine of an image in degrees. + /// + /// A new . + public Image Cos() => Math(Enums.OperationMath.Cos); + + /// + /// Return the tangent of an image in degrees. + /// + /// A new . + public Image Tan() => Math(Enums.OperationMath.Tan); + + /// + /// Return the inverse sine of an image in degrees. + /// + /// A new . + public Image Asin() => Math(Enums.OperationMath.Asin); + + /// + /// Return the inverse cosine of an image in degrees. + /// + /// A new . + public Image Acos() => Math(Enums.OperationMath.Acos); + + /// + /// Return the inverse tangent of an image in degrees. + /// + /// A new . + public Image Atan() => Math(Enums.OperationMath.Atan); + + /// + /// Return the hyperbolic sine of an image in radians. + /// + /// A new . + public Image Sinh() => Math(Enums.OperationMath.Sinh); + + /// + /// Return the hyperbolic cosine of an image in radians. + /// + /// A new . + public Image Cosh() => Math(Enums.OperationMath.Cosh); + + /// + /// Return the hyperbolic tangent of an image in radians. + /// + /// A new . + public Image Tanh() => Math(Enums.OperationMath.Tanh); + + /// + /// Return the inverse hyperbolic sine of an image in radians. + /// + /// A new . + public Image Asinh() => Math(Enums.OperationMath.Asinh); + + /// + /// Return the inverse hyperbolic cosine of an image in radians. + /// + /// A new . + public Image Acosh() => Math(Enums.OperationMath.Acosh); + + /// + /// Return the inverse hyperbolic tangent of an image in radians. + /// + /// A new . + public Image Atanh() => Math(Enums.OperationMath.Atanh); + + /// + /// Return the natural log of an image. + /// + /// A new . + public Image Log() => Math(Enums.OperationMath.Log); + + /// + /// Return the log base 10 of an image. + /// + /// A new . + public Image Log10() => Math(Enums.OperationMath.Log10); + + /// + /// Return e ** pixel. + /// + /// A new . + public Image Exp() => Math(Enums.OperationMath.Exp); + + /// + /// Return 10 ** pixel. + /// + /// A new . + public Image Exp10() => Math(Enums.OperationMath.Exp10); + + /// + /// Raise to power of an image. + /// + /// To the power of this. + /// A new . + public Image Pow(Image exp) => Math2(exp, Enums.OperationMath2.Pow); + + /// + /// Raise to power of an constant. + /// + /// To the power of this. + /// A new . + public Image Pow(double exp) => Math2Const(Enums.OperationMath2.Pow, new[] { exp }); + + /// + /// Raise to power of an array. + /// + /// To the power of this. + /// A new . + public Image Pow(double[] exp) => Math2Const(Enums.OperationMath2.Pow, exp); + + /// + /// Raise to power of an array. + /// + /// To the power of this. + /// A new . + public Image Pow(int[] exp) => + Math2Const(Enums.OperationMath2.Pow, Array.ConvertAll(exp, Convert.ToDouble)); + + /// + /// Raise to power of an image, but with the arguments reversed. + /// + /// To the base of this. + /// A new . + public Image Wop(Image @base) => Math2(@base, Enums.OperationMath2.Wop); + + /// + /// Raise to power of an constant, but with the arguments reversed. + /// + /// To the base of this. + /// A new . + public Image Wop(double @base) => Math2Const(Enums.OperationMath2.Wop, new[] { @base }); + + /// + /// Raise to power of an array, but with the arguments reversed. + /// + /// To the base of this. + /// A new . + public Image Wop(double[] @base) => Math2Const(Enums.OperationMath2.Wop, @base); + + /// + /// Raise to power of an array, but with the arguments reversed. + /// + /// To the base of this. + /// A new . + public Image Wop(int[] @base) => + Math2Const(Enums.OperationMath2.Wop, Array.ConvertAll(@base, Convert.ToDouble)); + + /// + /// Arc tangent of an image in degrees. + /// + /// Arc tangent of y / . + /// A new . + public Image Atan2(Image x) => Math2(x, Enums.OperationMath2.Atan2); + + /// + /// Arc tangent of an constant in degrees. + /// + /// Arc tangent of y / . + /// A new . + public Image Atan2(double x) => Math2Const(Enums.OperationMath2.Atan2, new[] { x }); + + /// + /// Arc tangent of an array in degrees. + /// + /// Arc tangent of y / . + /// A new . + public Image Atan2(double[] x) => Math2Const(Enums.OperationMath2.Atan2, x); + + /// + /// Arc tangent of an array in degrees. + /// + /// Arc tangent of y / . + /// A new . + public Image Atan2(int[] x) => + Math2Const(Enums.OperationMath2.Atan2, Array.ConvertAll(x, Convert.ToDouble)); + + /// + /// Erode with a structuring element. + /// + /// The structuring element. + /// A new . + public Image Erode(Image mask) => Morph(mask, Enums.OperationMorphology.Erode); + + /// + /// Dilate with a structuring element. + /// + /// The structuring element. + /// A new . + public Image Dilate(Image mask) => Morph(mask, Enums.OperationMorphology.Dilate); + + /// + /// size x size median filter. + /// + /// The median filter. + /// A new . + public Image Median(int size) => Rank(size, size, size * size / 2); + + /// + /// Flip horizontally. + /// + /// A new . + public Image FlipHor() => Flip(Enums.Direction.Horizontal); + + /// + /// Flip vertically. + /// + /// A new . + public Image FlipVer() => Flip(Enums.Direction.Vertical); + + /// + /// Rotate 90 degrees clockwise. + /// + /// A new . + public Image Rot90() => Rot(Enums.Angle.D90); + + /// + /// Rotate 180 degrees. + /// + /// A new . + public Image Rot180() => Rot(Enums.Angle.D180); + + /// + /// Rotate 270 degrees clockwise. + /// + /// A new . + public Image Rot270() => Rot(Enums.Angle.D270); + + /// + /// Return the largest integral value not greater than the argument. + /// + /// A new . + public Image Floor() => this.Call("round", Enums.OperationRound.Floor) as Image; + + /// + /// Return the largest integral value not greater than the argument. + /// + /// A new . + public Image Ceil() => this.Call("round", Enums.OperationRound.Ceil) as Image; + + /// + /// Return the nearest integral value. + /// + /// A new . + public Image Rint() => this.Call("round", Enums.OperationRound.Rint) as Image; + + /// + /// AND image bands together. + /// + /// A new . + public Image BandAnd() => this.Call("bandbool", Enums.OperationBoolean.And) as Image; + + /// + /// OR image bands together. + /// + /// A new . + public Image BandOr() => this.Call("bandbool", Enums.OperationBoolean.Or) as Image; + + /// + /// EOR image bands together. + /// + /// A new . + public Image BandEor() => this.Call("bandbool", Enums.OperationBoolean.Eor) as Image; + + /// + /// This operation compares two images on equality. + /// + /// A to compare. + /// A new . + public Image Equal(Image right) => + this.Call("relational", right, Enums.OperationRelational.Equal) as Image; + + /// + /// This operation compares two images on equality. + /// + /// A double array to compare. + /// A new . + public Image Equal(double[] right) => + this.Call("relational_const", Enums.OperationRelational.Equal, right) as Image; + + /// + /// This operation compares two images on equality. + /// + /// A integer array to compare. + /// A new . + public Image Equal(int[] right) => + this.Call("relational_const", Enums.OperationRelational.Equal, right) as Image; + + /// + /// This operation compares two images on equality. + /// + /// A double constant to compare. + /// A new . + public Image Equal(double right) => + this.Call("relational_const", Enums.OperationRelational.Equal, right) as Image; + + /// + /// This operation compares two images on inequality. + /// + /// A to compare. + /// A new . + public Image NotEqual(Image right) => + this.Call("relational", right, Enums.OperationRelational.Noteq) as Image; + + /// + /// This operation compares two images on inequality. + /// + /// A double constant to compare. + /// A new . + public Image NotEqual(double right) => + this.Call("relational_const", Enums.OperationRelational.Noteq, right) as Image; + + /// + /// This operation compares two images on inequality. + /// + /// A double array to compare. + /// A new . + public Image NotEqual(double[] right) => + this.Call("relational_const", Enums.OperationRelational.Noteq, right) as Image; + + /// + /// This operation compares two images on inequality. + /// + /// A integer array to compare. + /// A new . + public Image NotEqual(int[] right) => + this.Call("relational_const", Enums.OperationRelational.Noteq, right) as Image; + + /// + /// Does this image have an alpha channel? + /// + /// + /// Uses colour space interpretation with number of channels to guess + /// this. + /// + /// if this image has an alpha channel; + /// otherwise, . + public bool HasAlpha() + { + // use `vips_image_hasalpha` on libvips >= 8.5. + if (NetVips.AtLeastLibvips(8, 5)) { - var v = Min(out var x, out var y); - return new[] { v, x, y }; + return VipsImage.HasAlpha(this); } - /// - /// Return the real part of a complex image. - /// - /// A new . - public Image Real() => Complexget(Enums.OperationComplexget.Real); - - /// - /// Return the imaginary part of a complex image. - /// - /// A new . - public Image Imag() => Complexget(Enums.OperationComplexget.Imag); - - /// - /// Return an image converted to polar coordinates. - /// - /// A new . - public Image Polar() => RunCmplx(x => x.Complex(Enums.OperationComplex.Polar), this); - - /// - /// Return an image converted to rectangular coordinates. - /// - /// A new . - public Image Rect() => RunCmplx(x => x.Complex(Enums.OperationComplex.Rect), this); - - /// - /// Return the complex conjugate of an image. - /// - /// A new . - public Image Conj() => Complex(Enums.OperationComplex.Conj); - - /// - /// Return the sine of an image in degrees. - /// - /// A new . - public Image Sin() => Math(Enums.OperationMath.Sin); - - /// - /// Return the cosine of an image in degrees. - /// - /// A new . - public Image Cos() => Math(Enums.OperationMath.Cos); - - /// - /// Return the tangent of an image in degrees. - /// - /// A new . - public Image Tan() => Math(Enums.OperationMath.Tan); - - /// - /// Return the inverse sine of an image in degrees. - /// - /// A new . - public Image Asin() => Math(Enums.OperationMath.Asin); - - /// - /// Return the inverse cosine of an image in degrees. - /// - /// A new . - public Image Acos() => Math(Enums.OperationMath.Acos); - - /// - /// Return the inverse tangent of an image in degrees. - /// - /// A new . - public Image Atan() => Math(Enums.OperationMath.Atan); - - /// - /// Return the hyperbolic sine of an image in radians. - /// - /// A new . - public Image Sinh() => Math(Enums.OperationMath.Sinh); - - /// - /// Return the hyperbolic cosine of an image in radians. - /// - /// A new . - public Image Cosh() => Math(Enums.OperationMath.Cosh); - - /// - /// Return the hyperbolic tangent of an image in radians. - /// - /// A new . - public Image Tanh() => Math(Enums.OperationMath.Tanh); - - /// - /// Return the inverse hyperbolic sine of an image in radians. - /// - /// A new . - public Image Asinh() => Math(Enums.OperationMath.Asinh); - - /// - /// Return the inverse hyperbolic cosine of an image in radians. - /// - /// A new . - public Image Acosh() => Math(Enums.OperationMath.Acosh); - - /// - /// Return the inverse hyperbolic tangent of an image in radians. - /// - /// A new . - public Image Atanh() => Math(Enums.OperationMath.Atanh); - - /// - /// Return the natural log of an image. - /// - /// A new . - public Image Log() => Math(Enums.OperationMath.Log); - - /// - /// Return the log base 10 of an image. - /// - /// A new . - public Image Log10() => Math(Enums.OperationMath.Log10); - - /// - /// Return e ** pixel. - /// - /// A new . - public Image Exp() => Math(Enums.OperationMath.Exp); - - /// - /// Return 10 ** pixel. - /// - /// A new . - public Image Exp10() => Math(Enums.OperationMath.Exp10); - - /// - /// Raise to power of an image. - /// - /// To the power of this. - /// A new . - public Image Pow(Image exp) => Math2(exp, Enums.OperationMath2.Pow); - - /// - /// Raise to power of an constant. - /// - /// To the power of this. - /// A new . - public Image Pow(double exp) => Math2Const(Enums.OperationMath2.Pow, new[] { exp }); - - /// - /// Raise to power of an array. - /// - /// To the power of this. - /// A new . - public Image Pow(double[] exp) => Math2Const(Enums.OperationMath2.Pow, exp); - - /// - /// Raise to power of an array. - /// - /// To the power of this. - /// A new . - public Image Pow(int[] exp) => - Math2Const(Enums.OperationMath2.Pow, Array.ConvertAll(exp, Convert.ToDouble)); - - /// - /// Raise to power of an image, but with the arguments reversed. - /// - /// To the base of this. - /// A new . - public Image Wop(Image @base) => Math2(@base, Enums.OperationMath2.Wop); - - /// - /// Raise to power of an constant, but with the arguments reversed. - /// - /// To the base of this. - /// A new . - public Image Wop(double @base) => Math2Const(Enums.OperationMath2.Wop, new[] { @base }); - - /// - /// Raise to power of an array, but with the arguments reversed. - /// - /// To the base of this. - /// A new . - public Image Wop(double[] @base) => Math2Const(Enums.OperationMath2.Wop, @base); - - /// - /// Raise to power of an array, but with the arguments reversed. - /// - /// To the base of this. - /// A new . - public Image Wop(int[] @base) => - Math2Const(Enums.OperationMath2.Wop, Array.ConvertAll(@base, Convert.ToDouble)); - - /// - /// Arc tangent of an image in degrees. - /// - /// Arc tangent of y / . - /// A new . - public Image Atan2(Image x) => Math2(x, Enums.OperationMath2.Atan2); - - /// - /// Arc tangent of an constant in degrees. - /// - /// Arc tangent of y / . - /// A new . - public Image Atan2(double x) => Math2Const(Enums.OperationMath2.Atan2, new[] { x }); - - /// - /// Arc tangent of an array in degrees. - /// - /// Arc tangent of y / . - /// A new . - public Image Atan2(double[] x) => Math2Const(Enums.OperationMath2.Atan2, x); - - /// - /// Arc tangent of an array in degrees. - /// - /// Arc tangent of y / . - /// A new . - public Image Atan2(int[] x) => - Math2Const(Enums.OperationMath2.Atan2, Array.ConvertAll(x, Convert.ToDouble)); - - /// - /// Erode with a structuring element. - /// - /// The structuring element. - /// A new . - public Image Erode(Image mask) => Morph(mask, Enums.OperationMorphology.Erode); - - /// - /// Dilate with a structuring element. - /// - /// The structuring element. - /// A new . - public Image Dilate(Image mask) => Morph(mask, Enums.OperationMorphology.Dilate); - - /// - /// size x size median filter. - /// - /// The median filter. - /// A new . - public Image Median(int size) => Rank(size, size, size * size / 2); - - /// - /// Flip horizontally. - /// - /// A new . - public Image FlipHor() => Flip(Enums.Direction.Horizontal); - - /// - /// Flip vertically. - /// - /// A new . - public Image FlipVer() => Flip(Enums.Direction.Vertical); - - /// - /// Rotate 90 degrees clockwise. - /// - /// A new . - public Image Rot90() => Rot(Enums.Angle.D90); - - /// - /// Rotate 180 degrees. - /// - /// A new . - public Image Rot180() => Rot(Enums.Angle.D180); - - /// - /// Rotate 270 degrees clockwise. - /// - /// A new . - public Image Rot270() => Rot(Enums.Angle.D270); - - /// - /// Return the largest integral value not greater than the argument. - /// - /// A new . - public Image Floor() => this.Call("round", Enums.OperationRound.Floor) as Image; - - /// - /// Return the largest integral value not greater than the argument. - /// - /// A new . - public Image Ceil() => this.Call("round", Enums.OperationRound.Ceil) as Image; - - /// - /// Return the nearest integral value. - /// - /// A new . - public Image Rint() => this.Call("round", Enums.OperationRound.Rint) as Image; - - /// - /// AND image bands together. - /// - /// A new . - public Image BandAnd() => this.Call("bandbool", Enums.OperationBoolean.And) as Image; - - /// - /// OR image bands together. - /// - /// A new . - public Image BandOr() => this.Call("bandbool", Enums.OperationBoolean.Or) as Image; - - /// - /// EOR image bands together. - /// - /// A new . - public Image BandEor() => this.Call("bandbool", Enums.OperationBoolean.Eor) as Image; - - /// - /// This operation compares two images on equality. - /// - /// A to compare. - /// A new . - public Image Equal(Image right) => - this.Call("relational", right, Enums.OperationRelational.Equal) as Image; - - /// - /// This operation compares two images on equality. - /// - /// A double array to compare. - /// A new . - public Image Equal(double[] right) => - this.Call("relational_const", Enums.OperationRelational.Equal, right) as Image; - - /// - /// This operation compares two images on equality. - /// - /// A integer array to compare. - /// A new . - public Image Equal(int[] right) => - this.Call("relational_const", Enums.OperationRelational.Equal, right) as Image; - - /// - /// This operation compares two images on equality. - /// - /// A double constant to compare. - /// A new . - public Image Equal(double right) => - this.Call("relational_const", Enums.OperationRelational.Equal, right) as Image; - - /// - /// This operation compares two images on inequality. - /// - /// A to compare. - /// A new . - public Image NotEqual(Image right) => - this.Call("relational", right, Enums.OperationRelational.Noteq) as Image; - - /// - /// This operation compares two images on inequality. - /// - /// A double constant to compare. - /// A new . - public Image NotEqual(double right) => - this.Call("relational_const", Enums.OperationRelational.Noteq, right) as Image; - - /// - /// This operation compares two images on inequality. - /// - /// A double array to compare. - /// A new . - public Image NotEqual(double[] right) => - this.Call("relational_const", Enums.OperationRelational.Noteq, right) as Image; - - /// - /// This operation compares two images on inequality. - /// - /// A integer array to compare. - /// A new . - public Image NotEqual(int[] right) => - this.Call("relational_const", Enums.OperationRelational.Noteq, right) as Image; - - /// - /// Does this image have an alpha channel? - /// - /// - /// Uses colour space interpretation with number of channels to guess - /// this. - /// - /// if this image has an alpha channel; - /// otherwise, . - public bool HasAlpha() - { - // use `vips_image_hasalpha` on libvips >= 8.5. - if (NetVips.AtLeastLibvips(8, 5)) + return Bands == 2 || + (Bands == 4 && Interpretation != Enums.Interpretation.Cmyk) || + Bands > 4; + } + + /// + /// Append an alpha channel to an image. + /// + /// + /// + /// using rgba = rgb.AddAlpha(); + /// + /// + /// A new . + public Image AddAlpha() + { + // use `vips_addalpha` on libvips >= 8.6. + if (NetVips.AtLeastLibvips(8, 6)) + { + var result = VipsImage.AddAlpha(this, out var vi); + if (result != 0) { - return VipsImage.HasAlpha(this); + throw new VipsException("unable to append alpha channel to image."); } - return Bands == 2 || - (Bands == 4 && Interpretation != Enums.Interpretation.Cmyk) || - Bands > 4; + return new Image(vi); } - /// - /// Append an alpha channel to an image. - /// - /// - /// - /// using rgba = rgb.AddAlpha(); - /// - /// - /// A new . - public Image AddAlpha() - { - // use `vips_addalpha` on libvips >= 8.6. - if (NetVips.AtLeastLibvips(8, 6)) - { - var result = VipsImage.AddAlpha(this, out var vi); - if (result != 0) - { - throw new VipsException("unable to append alpha channel to image."); - } - - return new Image(vi); - } + var maxAlpha = Interpretation == Enums.Interpretation.Grey16 || Interpretation == Enums.Interpretation.Rgb16 + ? 65535 + : 255; + return Bandjoin(maxAlpha); + } - var maxAlpha = Interpretation == Enums.Interpretation.Grey16 || Interpretation == Enums.Interpretation.Rgb16 - ? 65535 - : 255; - return Bandjoin(maxAlpha); + /// + /// If image has been killed (see ), set an error message, + /// clear the `kill` flag and return . + /// Otherwise return . + /// + /// + /// Handy for loops which need to run sets of threads which can fail. + /// At least libvips 8.8 is needed. If this version requirement is not met, + /// it will always return . + /// + /// if image has been killed; + /// otherwise, . + public bool IsKilled() + { + if (!NetVips.AtLeastLibvips(8, 8)) + { + return false; } - /// - /// If image has been killed (see ), set an error message, - /// clear the `kill` flag and return . - /// Otherwise return . - /// - /// - /// Handy for loops which need to run sets of threads which can fail. - /// At least libvips 8.8 is needed. If this version requirement is not met, - /// it will always return . - /// - /// if image has been killed; - /// otherwise, . - public bool IsKilled() - { - if (!NetVips.AtLeastLibvips(8, 8)) - { - return false; - } + return VipsImage.IsKilled(this); + } - return VipsImage.IsKilled(this); + /// + /// Set the `kill` flag on an image. Handy for stopping sets of threads. + /// + /// + /// At least libvips 8.8 is needed. + /// + /// The kill state. + public void SetKill(bool kill) + { + if (!NetVips.AtLeastLibvips(8, 8)) + { + return; } - /// - /// Set the `kill` flag on an image. Handy for stopping sets of threads. - /// - /// - /// At least libvips 8.8 is needed. - /// - /// The kill state. - public void SetKill(bool kill) + VipsImage.SetKill(this, kill); + } + + /// + /// Connects a callback function () to a signal on this image. + /// + /// + /// The callback will be triggered every time this signal is issued on this image. + /// + /// A signal to be used on this image. See . + /// The callback to connect. + /// Data to pass to handler calls. + /// The handler id. + /// If it failed to connect the signal. + public ulong SignalConnect(Enums.Signals signal, EvalDelegate callback, nint data = default) + { + void EvalMarshal(nint imagePtr, VipsProgress progress, nint userDataPtr) { - if (!NetVips.AtLeastLibvips(8, 8)) + if (imagePtr == IntPtr.Zero) { return; } - VipsImage.SetKill(this, kill); - } + using var image = new Image(imagePtr); + image.ObjectRef(); - /// - /// Connects a callback function () to a signal on this image. - /// - /// - /// The callback will be triggered every time this signal is issued on this image. - /// - /// A signal to be used on this image. See . - /// The callback to connect. - /// Data to pass to handler calls. - /// The handler id. - /// If it failed to connect the signal. - public ulong SignalConnect(Enums.Signals signal, EvalDelegate callback, nint data = default) - { - void EvalMarshal(nint imagePtr, VipsProgress progress, nint userDataPtr) - { - if (imagePtr == IntPtr.Zero) - { - return; - } + callback.Invoke(image, progress); + } - using var image = new Image(imagePtr); - image.ObjectRef(); + return signal switch + { + Enums.Signals.PreEval => SignalConnect("preeval", EvalMarshal, data), + Enums.Signals.Eval => SignalConnect("eval", EvalMarshal, data), + Enums.Signals.PostEval => SignalConnect("posteval", EvalMarshal, data), + _ => throw new ArgumentOutOfRangeException(nameof(signal), signal, + $"The value of argument '{nameof(signal)}' ({signal}) is invalid for enum type '{nameof(Enums.Signals)}'.") + }; + } - callback.Invoke(image, progress); - } + /// + /// Drop caches on an image, and any downstream images. + /// + /// + /// This method drops all pixel caches on an image and on all downstream + /// images. Any operations which depend on this image, directly or + /// indirectly, are also dropped from the libvips operation cache. + /// + /// This method can be useful if you wrap a libvips image around an array + /// with + /// and then change some bytes without libvips knowing. + /// + public void Invalidate() + { + VipsImage.InvalidateAll(this); + } - return signal switch - { - Enums.Signals.PreEval => SignalConnect("preeval", EvalMarshal, data), - Enums.Signals.Eval => SignalConnect("eval", EvalMarshal, data), - Enums.Signals.PostEval => SignalConnect("posteval", EvalMarshal, data), - _ => throw new ArgumentOutOfRangeException(nameof(signal), signal, - $"The value of argument '{nameof(signal)}' ({signal}) is invalid for enum type '{nameof(Enums.Signals)}'.") - }; - } + /// + /// Enable progress reporting on an image. + /// + /// + /// When progress reporting is enabled, evaluation of the most downstream + /// image from this image will report progress using the , + /// and signals. + /// + /// to enable progress reporting; + /// otherwise, . + public void SetProgress(bool progress) + { + VipsImage.SetProgress(this, progress); + } - /// - /// Drop caches on an image, and any downstream images. - /// - /// - /// This method drops all pixel caches on an image and on all downstream - /// images. Any operations which depend on this image, directly or - /// indirectly, are also dropped from the libvips operation cache. - /// - /// This method can be useful if you wrap a libvips image around an array - /// with - /// and then change some bytes without libvips knowing. - /// - public void Invalidate() - { - VipsImage.InvalidateAll(this); + /// + /// Attach progress feedback, if required. + /// + /// + /// You can use this function to update user-interfaces with + /// progress feedback, for example: + /// + /// using var image = Image.NewFromFile("huge.jpg", access: Enums.Access.Sequential); + /// + /// var progress = new Progress<int>(percent => + /// { + /// Console.Write($"\r{percent}% complete"); + /// }); + /// image.SetProgress(progress); + /// + /// image.Dzsave("image-pyramid"); + /// + /// + /// If a cancellation has been requested for this token (see ) + /// it will block the evaluation of this image on libvips >= 8.8 (see ). + /// If this version requirement is not met, it will only stop updating the progress. + /// + /// A provider for progress updates. + /// Cancellation token to block evaluation on this image. + public void SetProgress(IProgress progress, CancellationToken token = default) + { + SetProgress(progress != null); + if (progress == null) + { + return; } - /// - /// Enable progress reporting on an image. - /// - /// - /// When progress reporting is enabled, evaluation of the most downstream - /// image from this image will report progress using the , - /// and signals. - /// - /// to enable progress reporting; - /// otherwise, . - public void SetProgress(bool progress) - { - VipsImage.SetProgress(this, progress); - } + var lastPercent = 0; + var isKilled = false; - /// - /// Attach progress feedback, if required. - /// - /// - /// You can use this function to update user-interfaces with - /// progress feedback, for example: - /// - /// using var image = Image.NewFromFile("huge.jpg", access: Enums.Access.Sequential); - /// - /// var progress = new Progress<int>(percent => - /// { - /// Console.Write($"\r{percent}% complete"); - /// }); - /// image.SetProgress(progress); - /// - /// image.Dzsave("image-pyramid"); - /// - /// - /// If a cancellation has been requested for this token (see ) - /// it will block the evaluation of this image on libvips >= 8.8 (see ). - /// If this version requirement is not met, it will only stop updating the progress. - /// - /// A provider for progress updates. - /// Cancellation token to block evaluation on this image. - public void SetProgress(IProgress progress, CancellationToken token = default) - { - SetProgress(progress != null); - if (progress == null) + void EvalCallback(Image image, VipsProgress progressStruct) + { + // Block evaluation on this image if a cancellation + // has been requested for this token. + if (token.IsCancellationRequested) { + if (!isKilled) + { + image.SetKill(true); + isKilled = true; + } + return; } - var lastPercent = 0; - var isKilled = false; - - void EvalCallback(Image image, VipsProgress progressStruct) + if (progressStruct.Percent != lastPercent) { - // Block evaluation on this image if a cancellation - // has been requested for this token. - if (token.IsCancellationRequested) - { - if (!isKilled) - { - image.SetKill(true); - isKilled = true; - } + progress.Report(progressStruct.Percent); + lastPercent = progressStruct.Percent; + } + } - return; - } + SignalConnect(Enums.Signals.Eval, EvalCallback); + } - if (progressStruct.Percent != lastPercent) - { - progress.Report(progressStruct.Percent); - lastPercent = progressStruct.Percent; - } - } + #endregion - SignalConnect(Enums.Signals.Eval, EvalCallback); - } + #region handwritten properties - #endregion + /// + /// Multi-page images can have a page height. + /// If page-height is not set, it defaults to the image height. + /// + /// + /// At least libvips 8.8 is needed. + /// + public int PageHeight => VipsImage.GetPageHeight(this); - #region handwritten properties + #endregion - /// - /// Multi-page images can have a page height. - /// If page-height is not set, it defaults to the image height. - /// - /// - /// At least libvips 8.8 is needed. - /// - public int PageHeight => VipsImage.GetPageHeight(this); + #region support with in the most trivial way - #endregion + /// + /// Does band exist in image. + /// + /// The index to fetch. + /// true if the index exists. + public bool BandExists(int i) + { + return i >= 0 && i <= Bands - 1; + } - #region support with in the most trivial way + /// + /// Overload `[]`. + /// + /// + /// Use `[]` to pull out band elements from an image. For example: + /// + /// using var green = rgbImage[1]; + /// + /// Will make a new one-band image from band 1 (the middle band). + /// + /// The band element to pull out. + /// A new . + public Image this[int i] => BandExists(i) ? ExtractBand(i) : null; + + /// + /// A synonym for . + /// + /// + /// + /// double[] outArray = in[x, y]; + /// + /// + /// Point to read. + /// Point to read. + /// An array of doubles. + public double[] this[int x, int y] => Getpoint(x, y); - /// - /// Does band exist in image. - /// - /// The index to fetch. - /// true if the index exists. - public bool BandExists(int i) + /// + /// Split an n-band image into n separate images. + /// + /// An array of . + public Image[] Bandsplit() + { + var images = new Image[Bands]; + for (var i = 0; i < Bands; i++) { - return i >= 0 && i <= Bands - 1; + ref var image = ref images[i]; + image = this[i]; } - /// - /// Overload `[]`. - /// - /// - /// Use `[]` to pull out band elements from an image. For example: - /// - /// using var green = rgbImage[1]; - /// - /// Will make a new one-band image from band 1 (the middle band). - /// - /// The band element to pull out. - /// A new . - public Image this[int i] => BandExists(i) ? ExtractBand(i) : null; - - /// - /// A synonym for . - /// - /// - /// - /// double[] outArray = in[x, y]; - /// - /// - /// Point to read. - /// Point to read. - /// An array of doubles. - public double[] this[int x, int y] => Getpoint(x, y); - - /// - /// Split an n-band image into n separate images. - /// - /// An array of . - public Image[] Bandsplit() - { - var images = new Image[Bands]; - for (var i = 0; i < Bands; i++) - { - ref var image = ref images[i]; - image = this[i]; - } + return images; + } - return images; - } + /// + /// Compares the hashcode of two images. + /// + /// The to compare. + /// if equal; otherwise, . + public bool Equals(Image other) + { + return Equals(GetHashCode(), other.GetHashCode()); + } - /// - /// Compares the hashcode of two images. - /// - /// The to compare. - /// if equal; otherwise, . - public bool Equals(Image other) + /// + /// Determines whether the specified object is equal to the current image. + /// + /// The object to compare with the current image. + /// if the specified object is equal + /// to the current image; otherwise, . + public override bool Equals(object obj) + { + if (obj == null) { - return Equals(GetHashCode(), other.GetHashCode()); + return false; } - /// - /// Determines whether the specified object is equal to the current image. - /// - /// The object to compare with the current image. - /// if the specified object is equal - /// to the current image; otherwise, . - public override bool Equals(object obj) + if (ReferenceEquals(this, obj)) { - if (obj == null) - { - return false; - } - - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj.GetType() != GetType()) - { - return false; - } - - return Equals((Image)obj); + return true; } - /// - /// Serves as the default hash function. - /// - /// A hash code for the current image. - public override int GetHashCode() + if (obj.GetType() != GetType()) { - return ToString().GetHashCode(); + return false; } - #endregion + return Equals((Image)obj); } + + /// + /// Serves as the default hash function. + /// + /// A hash code for the current image. + public override int GetHashCode() + { + return ToString().GetHashCode(); + } + + #endregion } \ No newline at end of file diff --git a/src/NetVips/Internal/Enums.cs b/src/NetVips/Internal/Enums.cs index 9981ce0b..c3cc29de 100644 --- a/src/NetVips/Internal/Enums.cs +++ b/src/NetVips/Internal/Enums.cs @@ -1,42 +1,41 @@ -namespace NetVips.Internal -{ - using System; +using System; + +namespace NetVips.Internal; - internal static class Enums +internal static class Enums +{ + [Flags] + internal enum GParamFlags { - [Flags] - internal enum GParamFlags - { - G_PARAM_READABLE = 1 << 0, - G_PARAM_WRITABLE = 1 << 1, - G_PARAM_READWRITE = G_PARAM_READABLE | G_PARAM_WRITABLE, - G_PARAM_CONSTRUCT = 1 << 2, - G_PARAM_CONSTRUCT_ONLY = 1 << 3, - G_PARAM_LAX_VALIDATION = 1 << 4, - G_PARAM_STATIC_NAME = 1 << 5, - G_PARAM_PRIVATE = G_PARAM_STATIC_NAME, - G_PARAM_STATIC_NICK = 1 << 6, - G_PARAM_STATIC_BLURB = 1 << 7, - G_PARAM_EXPLICIT_NOTIFY = 1 << 30, - G_PARAM_DEPRECATED = 1 << 31 - } + G_PARAM_READABLE = 1 << 0, + G_PARAM_WRITABLE = 1 << 1, + G_PARAM_READWRITE = G_PARAM_READABLE | G_PARAM_WRITABLE, + G_PARAM_CONSTRUCT = 1 << 2, + G_PARAM_CONSTRUCT_ONLY = 1 << 3, + G_PARAM_LAX_VALIDATION = 1 << 4, + G_PARAM_STATIC_NAME = 1 << 5, + G_PARAM_PRIVATE = G_PARAM_STATIC_NAME, + G_PARAM_STATIC_NICK = 1 << 6, + G_PARAM_STATIC_BLURB = 1 << 7, + G_PARAM_EXPLICIT_NOTIFY = 1 << 30, + G_PARAM_DEPRECATED = 1 << 31 + } - [Flags] - internal enum GConnectFlags - { - G_CONNECT_AFTER = 1 << 0, - G_CONNECT_SWAPPED = 1 << 1 - } + [Flags] + internal enum GConnectFlags + { + G_CONNECT_AFTER = 1 << 0, + G_CONNECT_SWAPPED = 1 << 1 + } - [Flags] - internal enum GSignalMatchType - { - G_SIGNAL_MATCH_ID = 1 << 0, - G_SIGNAL_MATCH_DETAIL = 1 << 1, - G_SIGNAL_MATCH_CLOSURE = 1 << 2, - G_SIGNAL_MATCH_FUNC = 1 << 3, - G_SIGNAL_MATCH_DATA = 1 << 4, - G_SIGNAL_MATCH_UNBLOCKED = 1 << 5 - } + [Flags] + internal enum GSignalMatchType + { + G_SIGNAL_MATCH_ID = 1 << 0, + G_SIGNAL_MATCH_DETAIL = 1 << 1, + G_SIGNAL_MATCH_CLOSURE = 1 << 2, + G_SIGNAL_MATCH_FUNC = 1 << 3, + G_SIGNAL_MATCH_DATA = 1 << 4, + G_SIGNAL_MATCH_UNBLOCKED = 1 << 5 } } \ No newline at end of file diff --git a/src/NetVips/Internal/GLib.cs b/src/NetVips/Internal/GLib.cs index 0c7e0398..a0f6b459 100644 --- a/src/NetVips/Internal/GLib.cs +++ b/src/NetVips/Internal/GLib.cs @@ -1,47 +1,47 @@ -namespace NetVips.Internal +using System.Runtime.InteropServices; +using System.Security; +using NetVips.Interop; + +using LogLevelFlags = NetVips.Enums.LogLevelFlags; + +namespace NetVips.Internal; + +internal static class GLib { - using System.Runtime.InteropServices; - using System.Security; - using Interop; - using LogLevelFlags = global::NetVips.Enums.LogLevelFlags; - - internal static class GLib - { - [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate void LogFuncNative(nint logDomain, LogLevelFlags flags, nint message, - nint userData); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GLib, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_free")] - internal static extern void GFree(nint mem); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GLib, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_malloc")] - internal static extern nint GMalloc(ulong nBytes); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GLib, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_log_set_handler")] - internal static extern uint GLogSetHandler([MarshalAs(UnmanagedType.LPStr)] string logDomain, - LogLevelFlags flags, LogFuncNative logFunc, nint userData); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GLib, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_log_remove_handler")] - internal static extern void - GLogRemoveHandler([MarshalAs(UnmanagedType.LPStr)] string logDomain, uint handlerId); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GLib, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_log_set_always_fatal")] - internal static extern LogLevelFlags GLogSetAlwaysFatal(LogLevelFlags fatalMask); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GLib, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_log_set_fatal_mask")] - internal static extern LogLevelFlags GLogSetFatalMask( - [MarshalAs(UnmanagedType.LPStr)] string logDomain, LogLevelFlags fatalMask); - } + [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void LogFuncNative(nint logDomain, LogLevelFlags flags, nint message, + nint userData); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GLib, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_free")] + internal static extern void GFree(nint mem); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GLib, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_malloc")] + internal static extern nint GMalloc(ulong nBytes); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GLib, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_log_set_handler")] + internal static extern uint GLogSetHandler([MarshalAs(UnmanagedType.LPStr)] string logDomain, + LogLevelFlags flags, LogFuncNative logFunc, nint userData); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GLib, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_log_remove_handler")] + internal static extern void + GLogRemoveHandler([MarshalAs(UnmanagedType.LPStr)] string logDomain, uint handlerId); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GLib, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_log_set_always_fatal")] + internal static extern LogLevelFlags GLogSetAlwaysFatal(LogLevelFlags fatalMask); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GLib, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_log_set_fatal_mask")] + internal static extern LogLevelFlags GLogSetFatalMask( + [MarshalAs(UnmanagedType.LPStr)] string logDomain, LogLevelFlags fatalMask); } \ No newline at end of file diff --git a/src/NetVips/Internal/GObject.cs b/src/NetVips/Internal/GObject.cs index 63d9e7c3..ce12782b 100644 --- a/src/NetVips/Internal/GObject.cs +++ b/src/NetVips/Internal/GObject.cs @@ -1,266 +1,266 @@ -namespace NetVips.Internal -{ - using System.Runtime.InteropServices; - using System.Security; - using Interop; - using GObjectManaged = global::NetVips.GObject; - using VipsBlobManaged = global::NetVips.VipsBlob; +using System.Runtime.InteropServices; +using System.Security; +using NetVips.Interop; - [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate void GWeakNotify(nint data, nint objectPointer); +using GObjectManaged = NetVips.GObject; +using VipsBlobManaged = NetVips.VipsBlob; - internal static class GObject - { - [StructLayout(LayoutKind.Sequential)] - internal struct Struct - { - internal GType.Instance GTypeInstance; - - internal uint RefCount; - - internal nint QData; - } - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_object_set_property")] - internal static extern void SetProperty(GObjectManaged @object, - [MarshalAs(UnmanagedType.LPStr)] string propertyName, in GValue.Struct value); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_object_get_property")] - internal static extern void GetProperty(GObjectManaged @object, - [MarshalAs(UnmanagedType.LPStr)] string propertyName, ref GValue.Struct value); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_object_ref")] - internal static extern nint Ref(nint @object); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_object_unref")] - internal static extern void Unref(nint @object); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_object_weak_ref")] - internal static extern void WeakRef(GObjectManaged @object, GWeakNotify notify, nint data); - } +namespace NetVips.Internal; + +[SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] +internal delegate void GWeakNotify(nint data, nint objectPointer); +internal static class GObject +{ [StructLayout(LayoutKind.Sequential)] - internal struct GEnumValue + internal struct Struct { - internal int Value; + internal GType.Instance GTypeInstance; - [MarshalAs(UnmanagedType.LPStr)] - internal string ValueName; + internal uint RefCount; - [MarshalAs(UnmanagedType.LPStr)] - internal string ValueNick; + internal nint QData; } - [StructLayout(LayoutKind.Sequential)] - internal struct GEnumClass - { - internal nint GTypeClass; + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_object_set_property")] + internal static extern void SetProperty(GObjectManaged @object, + [MarshalAs(UnmanagedType.LPStr)] string propertyName, in GValue.Struct value); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_object_get_property")] + internal static extern void GetProperty(GObjectManaged @object, + [MarshalAs(UnmanagedType.LPStr)] string propertyName, ref GValue.Struct value); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_object_ref")] + internal static extern nint Ref(nint @object); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_object_unref")] + internal static extern void Unref(nint @object); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_object_weak_ref")] + internal static extern void WeakRef(GObjectManaged @object, GWeakNotify notify, nint data); +} + +[StructLayout(LayoutKind.Sequential)] +internal struct GEnumValue +{ + internal int Value; - internal int Minimum; - internal int Maximum; - internal uint NValues; + [MarshalAs(UnmanagedType.LPStr)] + internal string ValueName; - internal nint Values; - } + [MarshalAs(UnmanagedType.LPStr)] + internal string ValueNick; +} + +[StructLayout(LayoutKind.Sequential)] +internal struct GEnumClass +{ + internal nint GTypeClass; + + internal int Minimum; + internal int Maximum; + internal uint NValues; - internal static class GType + internal nint Values; +} + +internal static class GType +{ + [StructLayout(LayoutKind.Sequential)] + internal struct Instance { - [StructLayout(LayoutKind.Sequential)] - internal struct Instance - { - internal nint GClass; - } - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_type_name")] - internal static extern nint Name(nint type); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_type_from_name")] - internal static extern nint FromName([MarshalAs(UnmanagedType.LPStr)] string name); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_type_fundamental")] - internal static extern nint Fundamental(nint typeId); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_type_class_ref")] - internal static extern nint ClassRef(nint type); + internal nint GClass; } - internal static class GValue + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_type_name")] + internal static extern nint Name(nint type); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_type_from_name")] + internal static extern nint FromName([MarshalAs(UnmanagedType.LPStr)] string name); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_type_fundamental")] + internal static extern nint Fundamental(nint typeId); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_type_class_ref")] + internal static extern nint ClassRef(nint type); +} + +internal static class GValue +{ + [StructLayout(LayoutKind.Explicit, Size = 24)] + internal struct Struct { - [StructLayout(LayoutKind.Explicit, Size = 24)] - internal struct Struct - { - [FieldOffset(0)] - internal nint GType; - - [FieldOffset(8)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - internal nint[] Data; - } - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_init")] - internal static extern nint Init(ref Struct value, nint gType); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_unset")] - internal static extern void Unset(ref Struct value); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_set_boolean")] - internal static extern void SetBoolean(ref Struct value, [MarshalAs(UnmanagedType.Bool)] bool vBoolean); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_get_boolean")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool GetBoolean(in Struct value); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_set_int")] - internal static extern void SetInt(ref Struct value, int vInt); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_get_int")] - internal static extern int GetInt(in Struct value); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_set_uint64")] - internal static extern void SetUint64(ref Struct value, ulong vUint64); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_get_uint64")] - internal static extern ulong GetUint64(in Struct value); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_set_double")] - internal static extern void SetDouble(ref Struct value, double vDouble); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_get_double")] - internal static extern double GetDouble(in Struct value); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_set_string")] - internal static extern void SetString(ref Struct value, byte[] vString); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_get_string")] - internal static extern nint GetString(in Struct value); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_set_enum")] - internal static extern void SetEnum(ref Struct value, int vEnum); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_get_enum")] - internal static extern int GetEnum(in Struct value); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_set_flags")] - internal static extern void SetFlags(ref Struct value, uint vFlags); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_get_flags")] - internal static extern uint GetFlags(in Struct value); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_set_object")] - internal static extern void SetObject(ref Struct value, GObjectManaged vObject); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_get_object")] - internal static extern nint GetObject(in Struct value); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_value_set_boxed")] - internal static extern void SetBoxed(ref Struct value, VipsBlobManaged boxed); + [FieldOffset(0)] + internal nint GType; + + [FieldOffset(8)] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + internal nint[] Data; } - internal static class GParamSpec + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_init")] + internal static extern nint Init(ref Struct value, nint gType); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_unset")] + internal static extern void Unset(ref Struct value); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_set_boolean")] + internal static extern void SetBoolean(ref Struct value, [MarshalAs(UnmanagedType.Bool)] bool vBoolean); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_get_boolean")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool GetBoolean(in Struct value); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_set_int")] + internal static extern void SetInt(ref Struct value, int vInt); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_get_int")] + internal static extern int GetInt(in Struct value); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_set_uint64")] + internal static extern void SetUint64(ref Struct value, ulong vUint64); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_get_uint64")] + internal static extern ulong GetUint64(in Struct value); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_set_double")] + internal static extern void SetDouble(ref Struct value, double vDouble); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_get_double")] + internal static extern double GetDouble(in Struct value); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_set_string")] + internal static extern void SetString(ref Struct value, byte[] vString); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_get_string")] + internal static extern nint GetString(in Struct value); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_set_enum")] + internal static extern void SetEnum(ref Struct value, int vEnum); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_get_enum")] + internal static extern int GetEnum(in Struct value); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_set_flags")] + internal static extern void SetFlags(ref Struct value, uint vFlags); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_get_flags")] + internal static extern uint GetFlags(in Struct value); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_set_object")] + internal static extern void SetObject(ref Struct value, GObjectManaged vObject); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_get_object")] + internal static extern nint GetObject(in Struct value); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_value_set_boxed")] + internal static extern void SetBoxed(ref Struct value, VipsBlobManaged boxed); +} + +internal static class GParamSpec +{ + [StructLayout(LayoutKind.Sequential)] + internal struct Struct { - [StructLayout(LayoutKind.Sequential)] - internal struct Struct - { - internal GType.Instance GTypeInstance; - - internal nint Name; - - internal Enums.GParamFlags Flags; - internal nint ValueType; - internal nint OwnerType; - - internal nint Nick; - internal nint Blurb; - internal nint QData; - internal uint RefCount; - internal uint ParamId; - } - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_param_spec_get_blurb")] - internal static extern nint GetBlurb(in Struct pspec); - } + internal GType.Instance GTypeInstance; - [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate void GClosureNotify(nint data, nint closure); + internal nint Name; - internal static class GSignal - { - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_signal_connect_data")] - internal static extern ulong ConnectData(GObjectManaged instance, - [MarshalAs(UnmanagedType.LPStr)] string detailedSignal, - nint cHandler, nint data, - GClosureNotify destroyData, Enums.GConnectFlags connectFlags); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_signal_handler_disconnect")] - internal static extern void HandlerDisconnect(GObjectManaged instance, ulong handlerId); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "g_signal_handlers_disconnect_matched")] - internal static extern uint HandlersDisconnectMatched(GObjectManaged instance, - Enums.GSignalMatchType mask, uint signalId, uint detail, nint closure, - nint func, nint data); + internal Enums.GParamFlags Flags; + internal nint ValueType; + internal nint OwnerType; + + internal nint Nick; + internal nint Blurb; + internal nint QData; + internal uint RefCount; + internal uint ParamId; } + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_param_spec_get_blurb")] + internal static extern nint GetBlurb(in Struct pspec); +} + +[SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] +internal delegate void GClosureNotify(nint data, nint closure); + +internal static class GSignal +{ + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_signal_connect_data")] + internal static extern ulong ConnectData(GObjectManaged instance, + [MarshalAs(UnmanagedType.LPStr)] string detailedSignal, + nint cHandler, nint data, + GClosureNotify destroyData, Enums.GConnectFlags connectFlags); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_signal_handler_disconnect")] + internal static extern void HandlerDisconnect(GObjectManaged instance, ulong handlerId); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.GObject, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "g_signal_handlers_disconnect_matched")] + internal static extern uint HandlersDisconnectMatched(GObjectManaged instance, + Enums.GSignalMatchType mask, uint signalId, uint detail, nint closure, + nint func, nint data); } \ No newline at end of file diff --git a/src/NetVips/Internal/Vips.cs b/src/NetVips/Internal/Vips.cs index 176ccbba..d4e340bc 100644 --- a/src/NetVips/Internal/Vips.cs +++ b/src/NetVips/Internal/Vips.cs @@ -1,688 +1,688 @@ -namespace NetVips.Internal -{ - using System.Runtime.InteropServices; - using System.Security; - using System.Text; - using Interop; - using VipsObjectManaged = global::NetVips.VipsObject; - using VipsBlobManaged = global::NetVips.VipsBlob; - using OperationFlags = global::NetVips.Enums.OperationFlags; - using ArgumentFlags = global::NetVips.Enums.ArgumentFlags; - using BandFormat = global::NetVips.Enums.BandFormat; - - internal static class Vips - { - [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate nint TypeMap2Fn(nint type, nint a, nint b); - - [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate nint ArgumentMapFn(nint @object, GParamSpec.Struct pspec, VipsArgumentClass argumentClass, - VipsArgumentInstance argumentInstance, nint a, nint b); - - [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate int CallbackFn(nint a, nint b); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_init")] - internal static extern int Init([MarshalAs(UnmanagedType.LPStr)] string argv0); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_shutdown")] - internal static extern void Shutdown(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_leak_set")] - internal static extern void LeakSet([MarshalAs(UnmanagedType.Bool)] bool leak); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_profile_set")] - internal static extern void ProfileSet([MarshalAs(UnmanagedType.Bool)] bool profile); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_cache_get_max")] - internal static extern int CacheGetMax(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_cache_set_max")] - internal static extern void CacheSetMax(int max); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_cache_get_max_mem")] - internal static extern nuint CacheGetMaxMem(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_cache_set_max_mem")] - internal static extern void CacheSetMaxMem(ulong maxMem); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_cache_get_max_files")] - internal static extern int CacheGetMaxFiles(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_cache_set_max_files")] - internal static extern void CacheSetMaxFiles(int maxFiles); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_cache_get_size")] - internal static extern int CacheGetSize(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_cache_set_trace")] - internal static extern void CacheSetTrace([MarshalAs(UnmanagedType.Bool)] bool trace); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_concurrency_set")] - internal static extern void ConcurrencySet(int concurrency); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_concurrency_get")] - internal static extern int ConcurrencyGet(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_vector_isenabled")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool VectorIsEnabled(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_vector_set_enabled")] - internal static extern void VectorSet([MarshalAs(UnmanagedType.Bool)] bool enabled); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_block_untrusted_set")] - internal static extern void BlockUntrustedSet([MarshalAs(UnmanagedType.Bool)] bool state); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_tracked_get_allocs")] - internal static extern int TrackedGetAllocs(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_tracked_get_mem")] - internal static extern int TrackedGetMem(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_tracked_get_files")] - internal static extern int TrackedGetFiles(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_tracked_get_mem_highwater")] - internal static extern ulong TrackedGetMemHighwater(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_version")] - internal static extern int Version(int flag); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_error_buffer")] - internal static extern nint ErrorBuffer(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_error_clear")] - internal static extern void ErrorClear(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_error_freeze")] - internal static extern void ErrorFreeze(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_error_thaw")] - internal static extern void ErrorThaw(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_path_filename7")] - internal static extern nint PathFilename7(byte[] path); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_path_mode7")] - internal static extern nint PathMode7(byte[] path); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_filename_get_filename")] - internal static extern nint GetFilename(byte[] vipsFilename); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_filename_get_options")] - internal static extern nint GetOptions(byte[] vipsFilename); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_blend_mode_get_type")] - internal static extern nint BlendModeGetType(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_interpretation_get_type")] - internal static extern nint InterpretationGetType(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_band_format_get_type")] - internal static extern nint BandFormatGetType(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_argument_map")] - internal static extern nint ArgumentMap(VipsObjectManaged @object, ArgumentMapFn fn, nint a, - nint b); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_type_map")] - internal static extern nint TypeMap(nint @base, TypeMap2Fn fn, nint a, nint b); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_type_find")] - internal static extern nint TypeFind([MarshalAs(UnmanagedType.LPStr)] string basename, - [MarshalAs(UnmanagedType.LPStr)] string nickname); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_nickname_find")] - internal static extern nint NicknameFind(nint type); - - internal static string PathFilename7(string path) - { - var bytes = Encoding.UTF8.GetBytes(path + char.MinValue); // Ensure null-terminated string - return PathFilename7(bytes).ToUtf8String(); - } - - internal static string PathMode7(string path) - { - var bytes = Encoding.UTF8.GetBytes(path + char.MinValue); // Ensure null-terminated string - return PathMode7(bytes).ToUtf8String(); - } - } +using System.Runtime.InteropServices; +using System.Security; +using System.Text; +using NetVips.Interop; - internal static class VipsObject - { - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_object_get_args")] - internal static extern int GetArgs(VipsObjectManaged @object, out nint names, out nint flags, - out int nArgs); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_object_get_argument")] - internal static extern int GetArgument(VipsObjectManaged @object, - [MarshalAs(UnmanagedType.LPStr)] string name, - out nint pspec, out VipsArgumentClass argumentClass, - out VipsArgumentInstance argumentInstance); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_object_set_from_string")] - internal static extern int SetFromString(VipsObjectManaged @object, - [MarshalAs(UnmanagedType.LPStr)] string @string); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_object_print_all")] - internal static extern void PrintAll(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_object_unref_outputs")] - internal static extern void UnrefOutputs(VipsObjectManaged @object); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_object_get_description")] - internal static extern nint GetDescription(VipsObjectManaged @object); - } +using ArgumentFlags = NetVips.Enums.ArgumentFlags; +using BandFormat = NetVips.Enums.BandFormat; +using OperationFlags = NetVips.Enums.OperationFlags; +using VipsBlobManaged = NetVips.VipsBlob; +using VipsObjectManaged = NetVips.VipsObject; - [StructLayout(LayoutKind.Sequential)] - internal struct VipsArgument - { - internal nint Pspec; - } +namespace NetVips.Internal; - [StructLayout(LayoutKind.Sequential)] - internal struct VipsArgumentClass - { - internal VipsArgument Parent; - internal nint ObjectClass; - internal ArgumentFlags Flags; - - internal int Priority; - internal uint Offset; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct VipsArgumentInstance +internal static class Vips +{ + [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate nint TypeMap2Fn(nint type, nint a, nint b); + + [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate nint ArgumentMapFn(nint @object, GParamSpec.Struct pspec, VipsArgumentClass argumentClass, + VipsArgumentInstance argumentInstance, nint a, nint b); + + [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate int CallbackFn(nint a, nint b); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_init")] + internal static extern int Init([MarshalAs(UnmanagedType.LPStr)] string argv0); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_shutdown")] + internal static extern void Shutdown(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_leak_set")] + internal static extern void LeakSet([MarshalAs(UnmanagedType.Bool)] bool leak); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_profile_set")] + internal static extern void ProfileSet([MarshalAs(UnmanagedType.Bool)] bool profile); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_cache_get_max")] + internal static extern int CacheGetMax(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_cache_set_max")] + internal static extern void CacheSetMax(int max); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_cache_get_max_mem")] + internal static extern nuint CacheGetMaxMem(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_cache_set_max_mem")] + internal static extern void CacheSetMaxMem(ulong maxMem); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_cache_get_max_files")] + internal static extern int CacheGetMaxFiles(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_cache_set_max_files")] + internal static extern void CacheSetMaxFiles(int maxFiles); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_cache_get_size")] + internal static extern int CacheGetSize(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_cache_set_trace")] + internal static extern void CacheSetTrace([MarshalAs(UnmanagedType.Bool)] bool trace); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_concurrency_set")] + internal static extern void ConcurrencySet(int concurrency); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_concurrency_get")] + internal static extern int ConcurrencyGet(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_vector_isenabled")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool VectorIsEnabled(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_vector_set_enabled")] + internal static extern void VectorSet([MarshalAs(UnmanagedType.Bool)] bool enabled); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_block_untrusted_set")] + internal static extern void BlockUntrustedSet([MarshalAs(UnmanagedType.Bool)] bool state); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_tracked_get_allocs")] + internal static extern int TrackedGetAllocs(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_tracked_get_mem")] + internal static extern int TrackedGetMem(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_tracked_get_files")] + internal static extern int TrackedGetFiles(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_tracked_get_mem_highwater")] + internal static extern ulong TrackedGetMemHighwater(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_version")] + internal static extern int Version(int flag); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_error_buffer")] + internal static extern nint ErrorBuffer(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_error_clear")] + internal static extern void ErrorClear(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_error_freeze")] + internal static extern void ErrorFreeze(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_error_thaw")] + internal static extern void ErrorThaw(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_path_filename7")] + internal static extern nint PathFilename7(byte[] path); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_path_mode7")] + internal static extern nint PathMode7(byte[] path); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_filename_get_filename")] + internal static extern nint GetFilename(byte[] vipsFilename); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_filename_get_options")] + internal static extern nint GetOptions(byte[] vipsFilename); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_blend_mode_get_type")] + internal static extern nint BlendModeGetType(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_interpretation_get_type")] + internal static extern nint InterpretationGetType(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_band_format_get_type")] + internal static extern nint BandFormatGetType(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_argument_map")] + internal static extern nint ArgumentMap(VipsObjectManaged @object, ArgumentMapFn fn, nint a, + nint b); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_type_map")] + internal static extern nint TypeMap(nint @base, TypeMap2Fn fn, nint a, nint b); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_type_find")] + internal static extern nint TypeFind([MarshalAs(UnmanagedType.LPStr)] string basename, + [MarshalAs(UnmanagedType.LPStr)] string nickname); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_nickname_find")] + internal static extern nint NicknameFind(nint type); + + internal static string PathFilename7(string path) { - internal VipsArgument Parent; - internal nint ArgumentClass; - internal nint Object; - - [MarshalAs(UnmanagedType.Bool)] - internal bool Assigned; - internal uint CloseId; - internal uint InvalidateId; + var bytes = Encoding.UTF8.GetBytes(path + char.MinValue); // Ensure null-terminated string + return PathFilename7(bytes).ToUtf8String(); } - internal static class VipsBlob + internal static string PathMode7(string path) { - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_blob_get")] - internal static extern nint Get(VipsBlobManaged blob, out nuint length); + var bytes = Encoding.UTF8.GetBytes(path + char.MinValue); // Ensure null-terminated string + return PathMode7(bytes).ToUtf8String(); } +} - internal static class VipsArea - { - [StructLayout(LayoutKind.Sequential)] - internal struct Struct - { - internal nint Data; - internal nuint Length; - - internal int N; +internal static class VipsObject +{ + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_object_get_args")] + internal static extern int GetArgs(VipsObjectManaged @object, out nint names, out nint flags, + out int nArgs); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_object_get_argument")] + internal static extern int GetArgument(VipsObjectManaged @object, + [MarshalAs(UnmanagedType.LPStr)] string name, + out nint pspec, out VipsArgumentClass argumentClass, + out VipsArgumentInstance argumentInstance); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_object_set_from_string")] + internal static extern int SetFromString(VipsObjectManaged @object, + [MarshalAs(UnmanagedType.LPStr)] string @string); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_object_print_all")] + internal static extern void PrintAll(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_object_unref_outputs")] + internal static extern void UnrefOutputs(VipsObjectManaged @object); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_object_get_description")] + internal static extern nint GetDescription(VipsObjectManaged @object); +} + +[StructLayout(LayoutKind.Sequential)] +internal struct VipsArgument +{ + internal nint Pspec; +} - // private +[StructLayout(LayoutKind.Sequential)] +internal struct VipsArgumentClass +{ + internal VipsArgument Parent; + internal nint ObjectClass; + internal ArgumentFlags Flags; - internal int Count; - internal nint Lock; + internal int Priority; + internal uint Offset; +} - internal Vips.CallbackFn FreeFn; - internal nint Client; +[StructLayout(LayoutKind.Sequential)] +internal struct VipsArgumentInstance +{ + internal VipsArgument Parent; + internal nint ArgumentClass; + internal nint Object; - internal nint Type; - internal nuint SizeofType; - } + [MarshalAs(UnmanagedType.Bool)] + internal bool Assigned; + internal uint CloseId; + internal uint InvalidateId; +} - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_area_unref")] - internal static extern nint Unref(nint blob); - } +internal static class VipsBlob +{ + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_blob_get")] + internal static extern nint Get(VipsBlobManaged blob, out nuint length); +} - internal static class VipsValue +internal static class VipsArea +{ + [StructLayout(LayoutKind.Sequential)] + internal struct Struct { - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_value_get_ref_string")] - internal static extern nint GetRefString(in GValue.Struct value, out ulong length); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_value_set_ref_string")] - internal static extern void SetRefString(ref GValue.Struct value, byte[] str); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_value_get_blob")] - internal static extern nint GetBlob(in GValue.Struct value, out ulong length); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_value_set_blob")] - internal static extern void SetBlob(ref GValue.Struct value, Vips.CallbackFn freeFn, - nint data, ulong length); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_value_set_blob_free")] - internal static extern void SetBlobFree(ref GValue.Struct value, nint data, - ulong length); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_value_get_array_double")] - internal static extern nint GetArrayDouble(in GValue.Struct value, out int n); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_value_set_array_double")] - internal static extern void SetArrayDouble(ref GValue.Struct value, - [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] - double[] array, int n); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_value_get_array_int")] - internal static extern nint GetArrayInt(in GValue.Struct value, out int n); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_value_set_array_int")] - internal static extern void SetArrayInt(ref GValue.Struct value, - [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] - int[] array, int n); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_value_get_array_image")] - internal static extern nint GetArrayImage(in GValue.Struct value, out int n); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_value_set_array_image")] - internal static extern void SetArrayImage(ref GValue.Struct value, int n); - } + internal nint Data; + internal nuint Length; - internal static class VipsImage - { - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_image_get_page_height")] - internal static extern int GetPageHeight(Image image); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_image_invalidate_all")] - internal static extern void InvalidateAll(Image image); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_image_set_progress")] - internal static extern void SetProgress(Image image, [MarshalAs(UnmanagedType.Bool)] bool progress); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_image_iskilled")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool IsKilled(Image image); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_image_set_kill")] - internal static extern void SetKill(Image image, [MarshalAs(UnmanagedType.Bool)] bool kill); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_image_new_from_memory")] - internal static extern nint NewFromMemory(nint data, nuint size, int width, int height, - int bands, BandFormat format); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_image_new_from_memory_copy")] - internal static extern nint NewFromMemoryCopy(nint data, nuint size, int width, int height, - int bands, BandFormat format); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_image_new_matrix_from_array")] - internal static extern nint NewMatrixFromArray(int width, int height, double[] array, int size); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_image_new_temp_file")] - internal static extern nint NewTempFile(byte[] format); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_image_write")] - internal static extern int Write(Image image, Image @out); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_image_write_to_memory")] - internal static extern nint WriteToMemory(Image @in, out ulong size); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_image_hasalpha")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool HasAlpha(Image image); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_addalpha")] - internal static extern int AddAlpha(Image image, out nint @out); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_image_copy_memory")] - internal static extern nint CopyMemory(Image image); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_image_set")] - internal static extern void Set(Image image, [MarshalAs(UnmanagedType.LPStr)] string name, - in GValue.Struct value); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_image_get")] - internal static extern int Get(Image image, [MarshalAs(UnmanagedType.LPStr)] string name, - out GValue.Struct valueCopy); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_image_get_typeof")] - internal static extern nint GetTypeof(Image image, [MarshalAs(UnmanagedType.LPStr)] string name); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_image_remove")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool Remove(Image image, [MarshalAs(UnmanagedType.LPStr)] string name); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_image_get_fields")] - internal static extern nint GetFields(Image image); - } + internal int N; - internal static class VipsInterpolate - { - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_interpolate_new")] - internal static extern nint New([MarshalAs(UnmanagedType.LPStr)] string nickname); - } + // private - internal static class VipsRegion - { - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_region_new")] - internal static extern nint New(Image image); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_region_fetch")] - internal static extern nint Fetch(Region region, int left, int top, int width, int height, out ulong length); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_region_width")] - internal static extern int Width(Region region); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_region_height")] - internal static extern int Height(Region region); - } + internal int Count; + internal nint Lock; - internal static class VipsOperation - { - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_operation_get_flags")] - internal static extern OperationFlags GetFlags(Operation operation); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_operation_new")] - internal static extern nint New([MarshalAs(UnmanagedType.LPStr)] string name); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_cache_operation_build")] - internal static extern nint Build(Operation operation); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_operation_flags_get_type")] - internal static extern nint FlagsGetType(); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_operation_block_set")] - internal static extern void BlockSet([MarshalAs(UnmanagedType.LPStr)] string name, - [MarshalAs(UnmanagedType.Bool)] bool state); - } + internal Vips.CallbackFn FreeFn; + internal nint Client; - internal static class VipsForeign - { - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_foreign_find_load")] - internal static extern nint FindLoad(nint filename); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_foreign_find_load")] - internal static extern nint FindLoad(byte[] filename); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_foreign_find_load_buffer")] - internal static extern nint FindLoadBuffer(byte[] data, ulong size); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_foreign_find_load_buffer")] - internal static extern nint FindLoadBuffer(nint data, ulong size); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_foreign_find_load_source")] - internal static extern nint FindLoadSource(Source stream); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_foreign_find_save")] - internal static extern nint FindSave(nint filename); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_foreign_find_save_buffer")] - internal static extern nint FindSaveBuffer(byte[] name); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_foreign_find_save_target")] - internal static extern nint FindSaveTarget(byte[] name); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_foreign_get_suffixes")] - internal static extern nint GetSuffixes(); + internal nint Type; + internal nuint SizeofType; } - internal static class VipsConnection - { - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_connection_filename")] - internal static extern nint FileName(Connection connection); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_connection_nick")] - internal static extern nint Nick(Connection connection); - } + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_area_unref")] + internal static extern nint Unref(nint blob); +} - internal static class VipsSource - { - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_source_new_from_descriptor")] - internal static extern nint NewFromDescriptor(int descriptor); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_source_new_from_file")] - internal static extern nint NewFromFile(byte[] filename); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_source_new_from_memory")] - internal static extern nint NewFromMemory(nint data, nuint size); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_source_map_blob")] - internal static extern nint MapBlob(Source source); - } +internal static class VipsValue +{ + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_value_get_ref_string")] + internal static extern nint GetRefString(in GValue.Struct value, out ulong length); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_value_set_ref_string")] + internal static extern void SetRefString(ref GValue.Struct value, byte[] str); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_value_get_blob")] + internal static extern nint GetBlob(in GValue.Struct value, out ulong length); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_value_set_blob")] + internal static extern void SetBlob(ref GValue.Struct value, Vips.CallbackFn freeFn, + nint data, ulong length); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_value_set_blob_free")] + internal static extern void SetBlobFree(ref GValue.Struct value, nint data, + ulong length); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_value_get_array_double")] + internal static extern nint GetArrayDouble(in GValue.Struct value, out int n); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_value_set_array_double")] + internal static extern void SetArrayDouble(ref GValue.Struct value, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] + double[] array, int n); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_value_get_array_int")] + internal static extern nint GetArrayInt(in GValue.Struct value, out int n); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_value_set_array_int")] + internal static extern void SetArrayInt(ref GValue.Struct value, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] + int[] array, int n); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_value_get_array_image")] + internal static extern nint GetArrayImage(in GValue.Struct value, out int n); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_value_set_array_image")] + internal static extern void SetArrayImage(ref GValue.Struct value, int n); +} + +internal static class VipsImage +{ + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_image_get_page_height")] + internal static extern int GetPageHeight(Image image); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_image_invalidate_all")] + internal static extern void InvalidateAll(Image image); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_image_set_progress")] + internal static extern void SetProgress(Image image, [MarshalAs(UnmanagedType.Bool)] bool progress); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_image_iskilled")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool IsKilled(Image image); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_image_set_kill")] + internal static extern void SetKill(Image image, [MarshalAs(UnmanagedType.Bool)] bool kill); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_image_new_from_memory")] + internal static extern nint NewFromMemory(nint data, nuint size, int width, int height, + int bands, BandFormat format); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_image_new_from_memory_copy")] + internal static extern nint NewFromMemoryCopy(nint data, nuint size, int width, int height, + int bands, BandFormat format); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_image_new_matrix_from_array")] + internal static extern nint NewMatrixFromArray(int width, int height, double[] array, int size); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_image_new_temp_file")] + internal static extern nint NewTempFile(byte[] format); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_image_write")] + internal static extern int Write(Image image, Image @out); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_image_write_to_memory")] + internal static extern nint WriteToMemory(Image @in, out ulong size); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_image_hasalpha")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool HasAlpha(Image image); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_addalpha")] + internal static extern int AddAlpha(Image image, out nint @out); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_image_copy_memory")] + internal static extern nint CopyMemory(Image image); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_image_set")] + internal static extern void Set(Image image, [MarshalAs(UnmanagedType.LPStr)] string name, + in GValue.Struct value); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_image_get")] + internal static extern int Get(Image image, [MarshalAs(UnmanagedType.LPStr)] string name, + out GValue.Struct valueCopy); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_image_get_typeof")] + internal static extern nint GetTypeof(Image image, [MarshalAs(UnmanagedType.LPStr)] string name); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_image_remove")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool Remove(Image image, [MarshalAs(UnmanagedType.LPStr)] string name); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_image_get_fields")] + internal static extern nint GetFields(Image image); +} + +internal static class VipsInterpolate +{ + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_interpolate_new")] + internal static extern nint New([MarshalAs(UnmanagedType.LPStr)] string nickname); +} - internal static class VipsSourceCustom - { - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_source_custom_new")] - internal static extern nint New(); +internal static class VipsRegion +{ + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_region_new")] + internal static extern nint New(Image image); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_region_fetch")] + internal static extern nint Fetch(Region region, int left, int top, int width, int height, out ulong length); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_region_width")] + internal static extern int Width(Region region); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_region_height")] + internal static extern int Height(Region region); +} + +internal static class VipsOperation +{ + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_operation_get_flags")] + internal static extern OperationFlags GetFlags(Operation operation); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_operation_new")] + internal static extern nint New([MarshalAs(UnmanagedType.LPStr)] string name); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_cache_operation_build")] + internal static extern nint Build(Operation operation); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_operation_flags_get_type")] + internal static extern nint FlagsGetType(); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_operation_block_set")] + internal static extern void BlockSet([MarshalAs(UnmanagedType.LPStr)] string name, + [MarshalAs(UnmanagedType.Bool)] bool state); +} + +internal static class VipsForeign +{ + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_foreign_find_load")] + internal static extern nint FindLoad(nint filename); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_foreign_find_load")] + internal static extern nint FindLoad(byte[] filename); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_foreign_find_load_buffer")] + internal static extern nint FindLoadBuffer(byte[] data, ulong size); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_foreign_find_load_buffer")] + internal static extern nint FindLoadBuffer(nint data, ulong size); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_foreign_find_load_source")] + internal static extern nint FindLoadSource(Source stream); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_foreign_find_save")] + internal static extern nint FindSave(nint filename); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_foreign_find_save_buffer")] + internal static extern nint FindSaveBuffer(byte[] name); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_foreign_find_save_target")] + internal static extern nint FindSaveTarget(byte[] name); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_foreign_get_suffixes")] + internal static extern nint GetSuffixes(); +} + +internal static class VipsConnection +{ + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_connection_filename")] + internal static extern nint FileName(Connection connection); - [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate long ReadSignal(nint sourcePtr, nint buffer, long length, nint userDataPtr); + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_connection_nick")] + internal static extern nint Nick(Connection connection); +} - [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate long SeekSignal(nint sourcePtr, long offset, int whence, nint userDataPtr); - } +internal static class VipsSource +{ + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_source_new_from_descriptor")] + internal static extern nint NewFromDescriptor(int descriptor); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_source_new_from_file")] + internal static extern nint NewFromFile(byte[] filename); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_source_new_from_memory")] + internal static extern nint NewFromMemory(nint data, nuint size); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_source_map_blob")] + internal static extern nint MapBlob(Source source); +} + +internal static class VipsSourceCustom +{ + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_source_custom_new")] + internal static extern nint New(); - internal static class VipsTarget - { - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_target_new_to_descriptor")] - internal static extern nint NewToDescriptor(int descriptor); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_target_new_to_file")] - internal static extern nint NewToFile(byte[] filename); - - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_target_new_to_memory")] - internal static extern nint NewToMemory(); - } + [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate long ReadSignal(nint sourcePtr, nint buffer, long length, nint userDataPtr); - internal static class VipsTargetCustom - { - [SuppressUnmanagedCodeSecurity] - [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "vips_target_custom_new")] - internal static extern nint New(); + [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate long SeekSignal(nint sourcePtr, long offset, int whence, nint userDataPtr); +} - [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate long WriteSignal(nint targetPtr, - [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] - byte[] buffer, int length, nint userDataPtr); +internal static class VipsTarget +{ + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_target_new_to_descriptor")] + internal static extern nint NewToDescriptor(int descriptor); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_target_new_to_file")] + internal static extern nint NewToFile(byte[] filename); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_target_new_to_memory")] + internal static extern nint NewToMemory(); +} + +internal static class VipsTargetCustom +{ + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_target_custom_new")] + internal static extern nint New(); - [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate long ReadSignal(nint targetPtr, nint buffer, long length, nint userDataPtr); + [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate long WriteSignal(nint targetPtr, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] + byte[] buffer, int length, nint userDataPtr); - [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate long SeekSignal(nint targetPtr, long offset, int whence, nint userDataPtr); + [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate long ReadSignal(nint targetPtr, nint buffer, long length, nint userDataPtr); - [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate int EndSignal(nint targetPtr, nint userDataPtr); - } + [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate long SeekSignal(nint targetPtr, long offset, int whence, nint userDataPtr); + + [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate int EndSignal(nint targetPtr, nint userDataPtr); } \ No newline at end of file diff --git a/src/NetVips/Interop/Libraries.cs b/src/NetVips/Interop/Libraries.cs index dcb80b4a..8c63eed7 100644 --- a/src/NetVips/Interop/Libraries.cs +++ b/src/NetVips/Interop/Libraries.cs @@ -1,13 +1,12 @@ -namespace NetVips.Interop +namespace NetVips.Interop; + +internal static class Libraries { - internal static class Libraries - { - /// - /// These library names are remapped in a cross-platform manner, - /// . - /// - internal const string GLib = "libglib-2.0-0.dll", - GObject = "libgobject-2.0-0.dll", - Vips = "libvips-42.dll"; - } + /// + /// These library names are remapped in a cross-platform manner, + /// . + /// + internal const string GLib = "libglib-2.0-0.dll", + GObject = "libgobject-2.0-0.dll", + Vips = "libvips-42.dll"; } \ No newline at end of file diff --git a/src/NetVips/Interpolate.cs b/src/NetVips/Interpolate.cs index df1c1e0c..23d8082c 100644 --- a/src/NetVips/Interpolate.cs +++ b/src/NetVips/Interpolate.cs @@ -1,45 +1,44 @@ -namespace NetVips +using System; +using NetVips.Internal; + +namespace NetVips; + +/// +/// Make interpolators for operators like . +/// +public class Interpolate : VipsObject { - using System; - using Internal; + private Interpolate(IntPtr pointer) + : base(pointer) + { + } /// - /// Make interpolators for operators like . + /// Make a new interpolator by name. /// - public class Interpolate : VipsObject + /// + /// Make a new interpolator from the libvips class nickname. For example: + /// + /// var inter = Interpolate.NewFromName("bicubic"); + /// + /// You can get a list of all supported interpolators from the command-line + /// with: + /// + /// $ vips -l interpolate + /// + /// See for example . + /// + /// libvips class nickname. + /// A new . + /// If unable to make a new interpolator from . + public static Interpolate NewFromName(string name) { - private Interpolate(IntPtr pointer) - : base(pointer) + var vi = VipsInterpolate.New(name); + if (vi == IntPtr.Zero) { + throw new VipsException($"no such interpolator {name}"); } - /// - /// Make a new interpolator by name. - /// - /// - /// Make a new interpolator from the libvips class nickname. For example: - /// - /// var inter = Interpolate.NewFromName("bicubic"); - /// - /// You can get a list of all supported interpolators from the command-line - /// with: - /// - /// $ vips -l interpolate - /// - /// See for example . - /// - /// libvips class nickname. - /// A new . - /// If unable to make a new interpolator from . - public static Interpolate NewFromName(string name) - { - var vi = VipsInterpolate.New(name); - if (vi == IntPtr.Zero) - { - throw new VipsException($"no such interpolator {name}"); - } - - return new Interpolate(vi); - } + return new Interpolate(vi); } } \ No newline at end of file diff --git a/src/NetVips/Introspect.cs b/src/NetVips/Introspect.cs index 6addba10..b4b4a19e 100644 --- a/src/NetVips/Introspect.cs +++ b/src/NetVips/Introspect.cs @@ -1,220 +1,219 @@ -namespace NetVips +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using NetVips.Internal; + +namespace NetVips; + +/// +/// Build introspection data for operations. +/// +/// +/// Make an operation, introspect it, and build a structure representing +/// everything we know about it. +/// +public class Introspect { - using System; - using System.Collections.Generic; - using System.Collections.Concurrent; - using System.Runtime.InteropServices; - using Internal; + /// + /// A cache for introspection data. + /// + private static readonly ConcurrentDictionary IntrospectCache = new(); /// - /// Build introspection data for operations. + /// An object structure that encapsulates the metadata + /// required to specify arguments. /// - /// - /// Make an operation, introspect it, and build a structure representing - /// everything we know about it. - /// - public class Introspect + public struct Argument { /// - /// A cache for introspection data. + /// Name of this argument. /// - private static readonly ConcurrentDictionary IntrospectCache = new(); + public string Name; /// - /// An object structure that encapsulates the metadata - /// required to specify arguments. + /// Flags for this argument. /// - public struct Argument - { - /// - /// Name of this argument. - /// - public string Name; - - /// - /// Flags for this argument. - /// - public Enums.ArgumentFlags Flags; - - /// - /// The GType for this argument. - /// - public nint Type; - } + public Enums.ArgumentFlags Flags; /// - /// The first required input image or . + /// The GType for this argument. /// - public Argument? MemberX; + public nint Type; + } - /// - /// A bool indicating if this operation is mutable. - /// - public bool Mutable; + /// + /// The first required input image or . + /// + public Argument? MemberX; - /// - /// The required input for this operation. - /// - public List RequiredInput = new(); + /// + /// A bool indicating if this operation is mutable. + /// + public bool Mutable; - /// - /// The optional input for this operation. - /// - public Dictionary OptionalInput = new(); + /// + /// The required input for this operation. + /// + public List RequiredInput = new(); - /// - /// The required output for this operation. - /// - public List RequiredOutput = new(); + /// + /// The optional input for this operation. + /// + public Dictionary OptionalInput = new(); - /// - /// The optional output for this operation. - /// - public Dictionary OptionalOutput = new(); + /// + /// The required output for this operation. + /// + public List RequiredOutput = new(); - /// - /// Build introspection data for a specified operation name. - /// - /// The operation name to introspect. - private Introspect(string operationName) + /// + /// The optional output for this operation. + /// + public Dictionary OptionalOutput = new(); + + /// + /// Build introspection data for a specified operation name. + /// + /// The operation name to introspect. + private Introspect(string operationName) + { + using var op = Operation.NewFromName(operationName); + var arguments = GetArgs(op); + + foreach (var entry in arguments) { - using var op = Operation.NewFromName(operationName); - var arguments = GetArgs(op); + var name = entry.Key; + var flag = entry.Value; + var gtype = op.GetTypeOf(name); - foreach (var entry in arguments) + var details = new Argument { - var name = entry.Key; - var flag = entry.Value; - var gtype = op.GetTypeOf(name); - - var details = new Argument - { - Name = name, - Flags = flag, - Type = gtype - }; + Name = name, + Flags = flag, + Type = gtype + }; - if ((flag & Enums.ArgumentFlags.INPUT) != 0) + if ((flag & Enums.ArgumentFlags.INPUT) != 0) + { + if ((flag & Enums.ArgumentFlags.REQUIRED) != 0 && + (flag & Enums.ArgumentFlags.DEPRECATED) == 0) { - if ((flag & Enums.ArgumentFlags.REQUIRED) != 0 && - (flag & Enums.ArgumentFlags.DEPRECATED) == 0) + // the first required input image arg will be self + if (!MemberX.HasValue && gtype == GValue.ImageType) { - // the first required input image arg will be self - if (!MemberX.HasValue && gtype == GValue.ImageType) - { - MemberX = details; - } - else - { - RequiredInput.Add(details); - } + MemberX = details; } else { - // we allow deprecated optional args - OptionalInput[name] = details; + RequiredInput.Add(details); } + } + else + { + // we allow deprecated optional args + OptionalInput[name] = details; + } - // modified input arguments count as mutable. - if ((flag & Enums.ArgumentFlags.MODIFY) != 0 && - (flag & Enums.ArgumentFlags.REQUIRED) != 0 && - (flag & Enums.ArgumentFlags.DEPRECATED) == 0) - { - Mutable = true; - } + // modified input arguments count as mutable. + if ((flag & Enums.ArgumentFlags.MODIFY) != 0 && + (flag & Enums.ArgumentFlags.REQUIRED) != 0 && + (flag & Enums.ArgumentFlags.DEPRECATED) == 0) + { + Mutable = true; + } + } + else if ((flag & Enums.ArgumentFlags.OUTPUT) != 0) + { + if ((flag & Enums.ArgumentFlags.REQUIRED) != 0 && + (flag & Enums.ArgumentFlags.DEPRECATED) == 0) + { + RequiredOutput.Add(details); } - else if ((flag & Enums.ArgumentFlags.OUTPUT) != 0) + else { - if ((flag & Enums.ArgumentFlags.REQUIRED) != 0 && - (flag & Enums.ArgumentFlags.DEPRECATED) == 0) - { - RequiredOutput.Add(details); - } - else - { - // again, allow deprecated optional args - OptionalOutput[name] = details; - } + // again, allow deprecated optional args + OptionalOutput[name] = details; } } } + } - /// - /// Get all arguments for an operation. - /// - /// - /// Not quick! Try to call this infrequently. - /// - /// Operation to lookup. - /// Arguments for the operation. - private IEnumerable> GetArgs(Operation operation) + /// + /// Get all arguments for an operation. + /// + /// + /// Not quick! Try to call this infrequently. + /// + /// Operation to lookup. + /// Arguments for the operation. + private IEnumerable> GetArgs(Operation operation) + { + var args = new List>(); + + void AddArg(string name, Enums.ArgumentFlags flags) { - var args = new List>(); + // libvips uses '-' to separate parts of arg names, but we + // need '_' for C# + name = name.Replace("-", "_"); - void AddArg(string name, Enums.ArgumentFlags flags) - { - // libvips uses '-' to separate parts of arg names, but we - // need '_' for C# - name = name.Replace("-", "_"); + args.Add(new KeyValuePair(name, flags)); + } - args.Add(new KeyValuePair(name, flags)); - } + // vips_object_get_args was added in 8.7 + if (NetVips.AtLeastLibvips(8, 7)) + { + var result = Internal.VipsObject.GetArgs(operation, out var names, out var flags, out var nArgs); - // vips_object_get_args was added in 8.7 - if (NetVips.AtLeastLibvips(8, 7)) + if (result != 0) { - var result = Internal.VipsObject.GetArgs(operation, out var names, out var flags, out var nArgs); + throw new VipsException("unable to get arguments from operation"); + } - if (result != 0) + for (var i = 0; i < nArgs; i++) + { + var flag = (Enums.ArgumentFlags)Marshal.PtrToStructure(flags + i * sizeof(int)); + if ((flag & Enums.ArgumentFlags.CONSTRUCT) == 0) { - throw new VipsException("unable to get arguments from operation"); + continue; } - for (var i = 0; i < nArgs; i++) - { - var flag = (Enums.ArgumentFlags)Marshal.PtrToStructure(flags + i * sizeof(int)); - if ((flag & Enums.ArgumentFlags.CONSTRUCT) == 0) - { - continue; - } + var name = Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(names, i * IntPtr.Size)); - var name = Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(names, i * IntPtr.Size)); - - AddArg(name, flag); - } + AddArg(name, flag); } - else + } + else + { + nint AddConstruct(nint self, GParamSpec.Struct pspec, VipsArgumentClass argumentClass, + VipsArgumentInstance argumentInstance, nint a, nint b) { - nint AddConstruct(nint self, GParamSpec.Struct pspec, VipsArgumentClass argumentClass, - VipsArgumentInstance argumentInstance, nint a, nint b) + var flags = argumentClass.Flags; + if ((flags & Enums.ArgumentFlags.CONSTRUCT) == 0) { - var flags = argumentClass.Flags; - if ((flags & Enums.ArgumentFlags.CONSTRUCT) == 0) - { - return IntPtr.Zero; - } - - var name = Marshal.PtrToStringAnsi(pspec.Name); - - AddArg(name, flags); - return IntPtr.Zero; } - Vips.ArgumentMap(operation, AddConstruct, IntPtr.Zero, IntPtr.Zero); + var name = Marshal.PtrToStringAnsi(pspec.Name); + + AddArg(name, flags); + + return IntPtr.Zero; } - return args; + Vips.ArgumentMap(operation, AddConstruct, IntPtr.Zero, IntPtr.Zero); } - /// - /// Get introspection data for a specified operation name. - /// - /// Operation name. - /// Introspection data. - public static Introspect Get(string operationName) - { - return IntrospectCache.GetOrAdd(operationName, name => new Introspect(name)); - } + return args; + } + + /// + /// Get introspection data for a specified operation name. + /// + /// Operation name. + /// Introspection data. + public static Introspect Get(string operationName) + { + return IntrospectCache.GetOrAdd(operationName, name => new Introspect(name)); } } \ No newline at end of file diff --git a/src/NetVips/Log.cs b/src/NetVips/Log.cs index 6d9f74a9..09a26975 100644 --- a/src/NetVips/Log.cs +++ b/src/NetVips/Log.cs @@ -1,139 +1,138 @@ -namespace NetVips +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Runtime.InteropServices; +using NetVips.Internal; + +namespace NetVips; + +/// +/// Wrapper for message logging functions. +/// +public static class Log { - using System; - using System.Collections.Concurrent; - using System.Diagnostics; - using System.Runtime.InteropServices; - using Internal; + private static GLib.LogFuncNative _nativeHandler; /// - /// Wrapper for message logging functions. + /// Specifies the prototype of log handler functions. /// - public static class Log - { - private static GLib.LogFuncNative _nativeHandler; + /// The log domain of the message. + /// The log level of the message (including the fatal and recursion flags). + /// The message to process. + public delegate void LogDelegate(string logDomain, Enums.LogLevelFlags logLevel, string message); - /// - /// Specifies the prototype of log handler functions. - /// - /// The log domain of the message. - /// The log level of the message (including the fatal and recursion flags). - /// The message to process. - public delegate void LogDelegate(string logDomain, Enums.LogLevelFlags logLevel, string message); - - private static void NativeCallback(nint logDomainNative, Enums.LogLevelFlags flags, nint messageNative, - nint userData) + private static void NativeCallback(nint logDomainNative, Enums.LogLevelFlags flags, nint messageNative, + nint userData) + { + if (userData == IntPtr.Zero) { - if (userData == IntPtr.Zero) - { - return; - } - - var logDomain = logDomainNative.ToUtf8String(); - var message = messageNative.ToUtf8String(); - var gch = (GCHandle)userData; - if (gch.Target is LogDelegate func) - { - func(logDomain, flags, message); - } + return; } - private static readonly ConcurrentDictionary _handlers = new(); - - /// - /// Sets the log handler for a domain and a set of log levels. - /// - /// The log domain, or for the default "" application domain. - /// The log levels to apply the log handler for. - /// The log handler function. - /// The id of the handler. - public static uint SetLogHandler(string logDomain, Enums.LogLevelFlags flags, LogDelegate logFunc) + var logDomain = logDomainNative.ToUtf8String(); + var message = messageNative.ToUtf8String(); + var gch = (GCHandle)userData; + if (gch.Target is LogDelegate func) { - _nativeHandler ??= NativeCallback; - - var gch = GCHandle.Alloc(logFunc); - var result = GLib.GLogSetHandler(logDomain, flags, _nativeHandler, (nint)gch); - _handlers.AddOrUpdate(result, gch, (_, _) => gch); - return result; + func(logDomain, flags, message); } + } - /// - /// Removes the log handler. - /// - /// The log domain. - /// The id of the handler, which was returned in . - public static void RemoveLogHandler(string logDomain, uint handlerId) - { - if (_handlers != null && - _handlers.ContainsKey(handlerId) && - _handlers.TryRemove(handlerId, out var handler)) - { - handler.Free(); - } + private static readonly ConcurrentDictionary _handlers = new(); - GLib.GLogRemoveHandler(logDomain, handlerId); - } + /// + /// Sets the log handler for a domain and a set of log levels. + /// + /// The log domain, or for the default "" application domain. + /// The log levels to apply the log handler for. + /// The log handler function. + /// The id of the handler. + public static uint SetLogHandler(string logDomain, Enums.LogLevelFlags flags, LogDelegate logFunc) + { + _nativeHandler ??= NativeCallback; - /// - /// Sets the message levels which are always fatal, in any log domain. - /// When a message with any of these levels is logged the program terminates. - /// - /// The mask containing bits set for each level of error which is to be fatal. - /// The old fatal mask. - public static Enums.LogLevelFlags SetAlwaysFatal(Enums.LogLevelFlags fatalMask) - { - return GLib.GLogSetAlwaysFatal(fatalMask); - } + var gch = GCHandle.Alloc(logFunc); + var result = GLib.GLogSetHandler(logDomain, flags, _nativeHandler, (nint)gch); + _handlers.AddOrUpdate(result, gch, (_, _) => gch); + return result; + } - /// - /// Sets the log levels which are fatal in the given domain. - /// - /// The log domain. - /// The new fatal mask. - /// The old fatal mask for the log domain. - public static Enums.LogLevelFlags SetAlwaysFatal(string logDomain, Enums.LogLevelFlags fatalMask) + /// + /// Removes the log handler. + /// + /// The log domain. + /// The id of the handler, which was returned in . + public static void RemoveLogHandler(string logDomain, uint handlerId) + { + if (_handlers != null && + _handlers.ContainsKey(handlerId) && + _handlers.TryRemove(handlerId, out var handler)) { - return GLib.GLogSetFatalMask(logDomain, fatalMask); + handler.Free(); } - /// - /// Common logging method. - /// - /// - /// Sample usage: - /// - /// // Print the messages for the NULL domain - /// var logFunc = new LogFunc(Log.PrintLogFunction); - /// Log.SetLogHandler(null, Enums.LogLevelFlags.All, logFunc); - /// - /// - /// The log domain of the message. - /// The log level of the message (including the fatal and recursion flags). - /// The message to process. - public static void PrintLogFunction(string domain, Enums.LogLevelFlags level, string message) - { - Console.WriteLine("Domain: '{0}' Level: {1}", domain, level); - Console.WriteLine("Message: {0}", message); - } + GLib.GLogRemoveHandler(logDomain, handlerId); + } - /// - /// Common logging method. - /// - /// - /// Sample usage: - /// - /// // Print messages and stack trace for vips critical messages - /// var logFunc = new LogFunc(Log.PrintTraceLogFunction); - /// Log.SetLogHandler("VIPS", Enums.LogLevelFlags.Critical, logFunc); - /// - /// - /// The log domain of the message. - /// The log level of the message (including the fatal and recursion flags). - /// The message to process. - public static void PrintTraceLogFunction(string domain, Enums.LogLevelFlags level, string message) - { - PrintLogFunction(domain, level, message); - Console.WriteLine("Trace follows:\n{0}", new StackTrace()); - } + /// + /// Sets the message levels which are always fatal, in any log domain. + /// When a message with any of these levels is logged the program terminates. + /// + /// The mask containing bits set for each level of error which is to be fatal. + /// The old fatal mask. + public static Enums.LogLevelFlags SetAlwaysFatal(Enums.LogLevelFlags fatalMask) + { + return GLib.GLogSetAlwaysFatal(fatalMask); + } + + /// + /// Sets the log levels which are fatal in the given domain. + /// + /// The log domain. + /// The new fatal mask. + /// The old fatal mask for the log domain. + public static Enums.LogLevelFlags SetAlwaysFatal(string logDomain, Enums.LogLevelFlags fatalMask) + { + return GLib.GLogSetFatalMask(logDomain, fatalMask); + } + + /// + /// Common logging method. + /// + /// + /// Sample usage: + /// + /// // Print the messages for the NULL domain + /// var logFunc = new LogFunc(Log.PrintLogFunction); + /// Log.SetLogHandler(null, Enums.LogLevelFlags.All, logFunc); + /// + /// + /// The log domain of the message. + /// The log level of the message (including the fatal and recursion flags). + /// The message to process. + public static void PrintLogFunction(string domain, Enums.LogLevelFlags level, string message) + { + Console.WriteLine("Domain: '{0}' Level: {1}", domain, level); + Console.WriteLine("Message: {0}", message); + } + + /// + /// Common logging method. + /// + /// + /// Sample usage: + /// + /// // Print messages and stack trace for vips critical messages + /// var logFunc = new LogFunc(Log.PrintTraceLogFunction); + /// Log.SetLogHandler("VIPS", Enums.LogLevelFlags.Critical, logFunc); + /// + /// + /// The log domain of the message. + /// The log level of the message (including the fatal and recursion flags). + /// The message to process. + public static void PrintTraceLogFunction(string domain, Enums.LogLevelFlags level, string message) + { + PrintLogFunction(domain, level, message); + Console.WriteLine("Trace follows:\n{0}", new StackTrace()); } } \ No newline at end of file diff --git a/src/NetVips/ModuleInitializer.cs b/src/NetVips/ModuleInitializer.cs index e78fa8d4..0e210636 100644 --- a/src/NetVips/ModuleInitializer.cs +++ b/src/NetVips/ModuleInitializer.cs @@ -1,131 +1,130 @@ -namespace NetVips -{ - using System; - using System.Reflection; - using System.Collections.Generic; - using System.Runtime.InteropServices; - using Interop; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using NetVips.Interop; + +namespace NetVips; +/// +/// All code inside the method is ran as soon as the assembly is loaded. +/// +public static class ModuleInitializer +{ /// - /// All code inside the method is ran as soon as the assembly is loaded. + /// Is vips initialized? /// - public static class ModuleInitializer - { - /// - /// Is vips initialized? - /// - public static bool VipsInitialized; + public static bool VipsInitialized; - /// - /// Contains the exception when initialization of libvips fails. - /// - public static Exception Exception; + /// + /// Contains the exception when initialization of libvips fails. + /// + public static Exception Exception; - /// - /// Could contain the version number of libvips in an 3-bytes integer. - /// - public static int? Version; + /// + /// Could contain the version number of libvips in an 3-bytes integer. + /// + public static int? Version; #if NET6_0_OR_GREATER - /// - /// Windows specific: is GLib statically-linked in `libvips-42.dll`? - /// - [System.Runtime.Versioning.SupportedOSPlatform("windows")] - private static bool _gLibStaticallyLinked = true; + /// + /// Windows specific: is GLib statically-linked in `libvips-42.dll`? + /// + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + private static bool _gLibStaticallyLinked = true; - /// - /// A cache for . - /// - internal static readonly Dictionary DllImportCache = new(); + /// + /// A cache for . + /// + internal static readonly Dictionary DllImportCache = new(); - internal static string RemapLibraryName(string libraryName) + internal static string RemapLibraryName(string libraryName) + { + // For Windows, we try to locate the GLib symbols within + // `libvips-42.dll` first. If these symbols cannot be found there, + // we proceed to locate them within `libglib-2.0-0.dll` and + // `libgobject-2.0-0.dll`. Note that distributing a single shared + // library is only possible when we drop support for .NET Framework. + // Therefore, we always ship at least 3 DLLs for now. + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - // For Windows, we try to locate the GLib symbols within - // `libvips-42.dll` first. If these symbols cannot be found there, - // we proceed to locate them within `libglib-2.0-0.dll` and - // `libgobject-2.0-0.dll`. Note that distributing a single shared - // library is only possible when we drop support for .NET Framework. - // Therefore, we always ship at least 3 DLLs for now. - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return _gLibStaticallyLinked ? Libraries.Vips : libraryName; - } - - // We can safely remap the library names to `libvips.so.42` on *nix - // and `libvips.42.dylib` on macOS since DLLImport uses dlsym() there. - // This function also searches for named symbols in the dependencies - // of the shared library. Therefore, we can provide libvips as a - // single shared library with all dependencies statically linked - // without breaking compatibility with the shared builds - // (i.e. what is usually installed via package managers). - return RuntimeInformation.IsOSPlatform(OSPlatform.OSX) - ? "libvips.42.dylib" - : "libvips.so.42"; + return _gLibStaticallyLinked ? Libraries.Vips : libraryName; } - internal static nint DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) - { - libraryName = RemapLibraryName(libraryName); - if (DllImportCache.TryGetValue(libraryName, out var cachedHandle)) - { - return cachedHandle; - } + // We can safely remap the library names to `libvips.so.42` on *nix + // and `libvips.42.dylib` on macOS since DLLImport uses dlsym() there. + // This function also searches for named symbols in the dependencies + // of the shared library. Therefore, we can provide libvips as a + // single shared library with all dependencies statically linked + // without breaking compatibility with the shared builds + // (i.e. what is usually installed via package managers). + return RuntimeInformation.IsOSPlatform(OSPlatform.OSX) + ? "libvips.42.dylib" + : "libvips.so.42"; + } - if (NativeLibrary.TryLoad(libraryName, assembly, searchPath, out var handle)) - { - DllImportCache[libraryName] = handle; - return handle; - } + internal static nint DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) + { + libraryName = RemapLibraryName(libraryName); + if (DllImportCache.TryGetValue(libraryName, out var cachedHandle)) + { + return cachedHandle; + } - // Fallback to the default import resolver. - return IntPtr.Zero; + if (NativeLibrary.TryLoad(libraryName, assembly, searchPath, out var handle)) + { + DllImportCache[libraryName] = handle; + return handle; } + + // Fallback to the default import resolver. + return IntPtr.Zero; + } #endif - /// - /// Initializes the module. - /// + /// + /// Initializes the module. + /// #pragma warning disable CA2255 - [System.Runtime.CompilerServices.ModuleInitializer] + [System.Runtime.CompilerServices.ModuleInitializer] #pragma warning restore CA2255 - public static void Initialize() - { + public static void Initialize() + { #if NET6_0_OR_GREATER - NativeLibrary.SetDllImportResolver(typeof(ModuleInitializer).Assembly, DllImportResolver); + NativeLibrary.SetDllImportResolver(typeof(ModuleInitializer).Assembly, DllImportResolver); #endif - try + try + { + VipsInitialized = NetVips.Init(); + if (VipsInitialized) { - VipsInitialized = NetVips.Init(); - if (VipsInitialized) - { - Version = NetVips.Version(0, false); - Version = (Version << 8) + NetVips.Version(1, false); - Version = (Version << 8) + NetVips.Version(2, false); + Version = NetVips.Version(0, false); + Version = (Version << 8) + NetVips.Version(1, false); + Version = (Version << 8) + NetVips.Version(2, false); #if NET6_0_OR_GREATER - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return; + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return; - try - { - _gLibStaticallyLinked = NetVips.TypeFromName("VipsImage") != IntPtr.Zero; - } - catch - { - _gLibStaticallyLinked = false; - } -#endif + try + { + _gLibStaticallyLinked = NetVips.TypeFromName("VipsImage") != IntPtr.Zero; } - else + catch { - Exception = new VipsException("unable to initialize libvips"); + _gLibStaticallyLinked = false; } +#endif } - catch (Exception e) + else { - VipsInitialized = false; - Exception = e; + Exception = new VipsException("unable to initialize libvips"); } } + catch (Exception e) + { + VipsInitialized = false; + Exception = e; + } } } \ No newline at end of file diff --git a/src/NetVips/ModuleInitializerAttribute.cs b/src/NetVips/ModuleInitializerAttribute.cs index 06d6cea3..4ad320c7 100644 --- a/src/NetVips/ModuleInitializerAttribute.cs +++ b/src/NetVips/ModuleInitializerAttribute.cs @@ -1,12 +1,11 @@ // This file enables ModuleInitializer, a C# 9 feature to work with .NET Framework. #if NETFRAMEWORK -namespace System.Runtime.CompilerServices +namespace System.Runtime.CompilerServices; + +/// +[AttributeUsage(AttributeTargets.Method, Inherited = false)] +internal sealed class ModuleInitializerAttribute : Attribute { - /// - [AttributeUsage(AttributeTargets.Method, Inherited = false)] - internal sealed class ModuleInitializerAttribute : Attribute - { - } } #endif \ No newline at end of file diff --git a/src/NetVips/MutableImage.Generated.cs b/src/NetVips/MutableImage.Generated.cs index 61687a58..6b399836 100644 --- a/src/NetVips/MutableImage.Generated.cs +++ b/src/NetVips/MutableImage.Generated.cs @@ -8,287 +8,286 @@ // //------------------------------------------------------------------------------ -namespace NetVips +namespace NetVips; + +public sealed partial class MutableImage { - public sealed partial class MutableImage + #region auto-generated functions + + /// + /// Draw a circle on an image. + /// + /// + /// + /// image.Mutate(x => x.DrawCircle(ink, cx, cy, radius, fill: bool)); + /// + /// + /// Color for pixels. + /// Centre of draw_circle. + /// Centre of draw_circle. + /// Radius in pixels. + /// Draw a solid object. + public void DrawCircle(double[] ink, int cx, int cy, int radius, bool? fill = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(fill), fill); + + this.Call("draw_circle", options, ink, cx, cy, radius); + } + + /// + /// Flood-fill an area. + /// + /// + /// + /// image.Mutate(x => x.DrawFlood(ink, x, y, test: Image, equal: bool)); + /// + /// + /// Color for pixels. + /// DrawFlood start point. + /// DrawFlood start point. + /// Test pixels in this image. + /// DrawFlood while equal to edge. + public void DrawFlood(double[] ink, int x, int y, Image test = null, bool? equal = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(test), test); + options.AddIfPresent(nameof(equal), equal); + + this.Call("draw_flood", options, ink, x, y); + } + + /// + /// Flood-fill an area. + /// + /// + /// + /// image.Mutate(x => x.DrawFlood(ink, x, y, out var left, test: Image, equal: bool)); + /// + /// + /// Color for pixels. + /// DrawFlood start point. + /// DrawFlood start point. + /// Left edge of modified area. + /// Test pixels in this image. + /// DrawFlood while equal to edge. + public void DrawFlood(double[] ink, int x, int y, out int left, Image test = null, bool? equal = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(test), test); + options.AddIfPresent(nameof(equal), equal); + + options.Add("left", true); + + var results = this.Call("draw_flood", options, ink, x, y) as object[]; + + var opts = results?[1] as VOption; + left = opts?["left"] is int out1 ? out1 : 0; + } + + /// + /// Flood-fill an area. + /// + /// + /// + /// image.Mutate(x => x.DrawFlood(ink, x, y, out var left, out var top, test: Image, equal: bool)); + /// + /// + /// Color for pixels. + /// DrawFlood start point. + /// DrawFlood start point. + /// Left edge of modified area. + /// Top edge of modified area. + /// Test pixels in this image. + /// DrawFlood while equal to edge. + public void DrawFlood(double[] ink, int x, int y, out int left, out int top, Image test = null, bool? equal = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(test), test); + options.AddIfPresent(nameof(equal), equal); + + options.Add("left", true); + options.Add("top", true); + + var results = this.Call("draw_flood", options, ink, x, y) as object[]; + + var opts = results?[1] as VOption; + left = opts?["left"] is int out1 ? out1 : 0; + top = opts?["top"] is int out2 ? out2 : 0; + } + + /// + /// Flood-fill an area. + /// + /// + /// + /// image.Mutate(x => x.DrawFlood(ink, x, y, out var left, out var top, out var width, test: Image, equal: bool)); + /// + /// + /// Color for pixels. + /// DrawFlood start point. + /// DrawFlood start point. + /// Left edge of modified area. + /// Top edge of modified area. + /// Width of modified area. + /// Test pixels in this image. + /// DrawFlood while equal to edge. + public void DrawFlood(double[] ink, int x, int y, out int left, out int top, out int width, Image test = null, bool? equal = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(test), test); + options.AddIfPresent(nameof(equal), equal); + + options.Add("left", true); + options.Add("top", true); + options.Add("width", true); + + var results = this.Call("draw_flood", options, ink, x, y) as object[]; + + var opts = results?[1] as VOption; + left = opts?["left"] is int out1 ? out1 : 0; + top = opts?["top"] is int out2 ? out2 : 0; + width = opts?["width"] is int out3 ? out3 : 0; + } + + /// + /// Flood-fill an area. + /// + /// + /// + /// image.Mutate(x => x.DrawFlood(ink, x, y, out var left, out var top, out var width, out var height, test: Image, equal: bool)); + /// + /// + /// Color for pixels. + /// DrawFlood start point. + /// DrawFlood start point. + /// Left edge of modified area. + /// Top edge of modified area. + /// Width of modified area. + /// Height of modified area. + /// Test pixels in this image. + /// DrawFlood while equal to edge. + public void DrawFlood(double[] ink, int x, int y, out int left, out int top, out int width, out int height, Image test = null, bool? equal = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(test), test); + options.AddIfPresent(nameof(equal), equal); + + options.Add("left", true); + options.Add("top", true); + options.Add("width", true); + options.Add("height", true); + + var results = this.Call("draw_flood", options, ink, x, y) as object[]; + + var opts = results?[1] as VOption; + left = opts?["left"] is int out1 ? out1 : 0; + top = opts?["top"] is int out2 ? out2 : 0; + width = opts?["width"] is int out3 ? out3 : 0; + height = opts?["height"] is int out4 ? out4 : 0; + } + + /// + /// Paint an image into another image. + /// + /// + /// + /// image.Mutate(x => x.DrawImage(sub, x, y, mode: Enums.CombineMode)); + /// + /// + /// Sub-image to insert into main image. + /// Draw image here. + /// Draw image here. + /// Combining mode. + public void DrawImage(Image sub, int x, int y, Enums.CombineMode? mode = null) + { + var options = new VOption(); + + options.AddIfPresent(nameof(mode), mode); + + this.Call("draw_image", options, sub, x, y); + } + + /// + /// Draw a line on an image. + /// + /// + /// + /// image.Mutate(x => x.DrawLine(ink, x1, y1, x2, y2)); + /// + /// + /// Color for pixels. + /// Start of draw_line. + /// Start of draw_line. + /// End of draw_line. + /// End of draw_line. + public void DrawLine(double[] ink, int x1, int y1, int x2, int y2) + { + this.Call("draw_line", ink, x1, y1, x2, y2); + } + + /// + /// Draw a mask on an image. + /// + /// + /// + /// image.Mutate(x => x.DrawMask(ink, mask, x, y)); + /// + /// + /// Color for pixels. + /// Mask of pixels to draw. + /// Draw mask here. + /// Draw mask here. + public void DrawMask(double[] ink, Image mask, int x, int y) + { + this.Call("draw_mask", ink, mask, x, y); + } + + /// + /// Paint a rectangle on an image. + /// + /// + /// + /// image.Mutate(x => x.DrawRect(ink, left, top, width, height, fill: bool)); + /// + /// + /// Color for pixels. + /// Rect to fill. + /// Rect to fill. + /// Rect to fill. + /// Rect to fill. + /// Draw a solid object. + public void DrawRect(double[] ink, int left, int top, int width, int height, bool? fill = null) { - #region auto-generated functions - - /// - /// Draw a circle on an image. - /// - /// - /// - /// image.Mutate(x => x.DrawCircle(ink, cx, cy, radius, fill: bool)); - /// - /// - /// Color for pixels. - /// Centre of draw_circle. - /// Centre of draw_circle. - /// Radius in pixels. - /// Draw a solid object. - public void DrawCircle(double[] ink, int cx, int cy, int radius, bool? fill = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(fill), fill); - - this.Call("draw_circle", options, ink, cx, cy, radius); - } - - /// - /// Flood-fill an area. - /// - /// - /// - /// image.Mutate(x => x.DrawFlood(ink, x, y, test: Image, equal: bool)); - /// - /// - /// Color for pixels. - /// DrawFlood start point. - /// DrawFlood start point. - /// Test pixels in this image. - /// DrawFlood while equal to edge. - public void DrawFlood(double[] ink, int x, int y, Image test = null, bool? equal = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(test), test); - options.AddIfPresent(nameof(equal), equal); - - this.Call("draw_flood", options, ink, x, y); - } - - /// - /// Flood-fill an area. - /// - /// - /// - /// image.Mutate(x => x.DrawFlood(ink, x, y, out var left, test: Image, equal: bool)); - /// - /// - /// Color for pixels. - /// DrawFlood start point. - /// DrawFlood start point. - /// Left edge of modified area. - /// Test pixels in this image. - /// DrawFlood while equal to edge. - public void DrawFlood(double[] ink, int x, int y, out int left, Image test = null, bool? equal = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(test), test); - options.AddIfPresent(nameof(equal), equal); - - options.Add("left", true); - - var results = this.Call("draw_flood", options, ink, x, y) as object[]; - - var opts = results?[1] as VOption; - left = opts?["left"] is int out1 ? out1 : 0; - } - - /// - /// Flood-fill an area. - /// - /// - /// - /// image.Mutate(x => x.DrawFlood(ink, x, y, out var left, out var top, test: Image, equal: bool)); - /// - /// - /// Color for pixels. - /// DrawFlood start point. - /// DrawFlood start point. - /// Left edge of modified area. - /// Top edge of modified area. - /// Test pixels in this image. - /// DrawFlood while equal to edge. - public void DrawFlood(double[] ink, int x, int y, out int left, out int top, Image test = null, bool? equal = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(test), test); - options.AddIfPresent(nameof(equal), equal); - - options.Add("left", true); - options.Add("top", true); - - var results = this.Call("draw_flood", options, ink, x, y) as object[]; - - var opts = results?[1] as VOption; - left = opts?["left"] is int out1 ? out1 : 0; - top = opts?["top"] is int out2 ? out2 : 0; - } - - /// - /// Flood-fill an area. - /// - /// - /// - /// image.Mutate(x => x.DrawFlood(ink, x, y, out var left, out var top, out var width, test: Image, equal: bool)); - /// - /// - /// Color for pixels. - /// DrawFlood start point. - /// DrawFlood start point. - /// Left edge of modified area. - /// Top edge of modified area. - /// Width of modified area. - /// Test pixels in this image. - /// DrawFlood while equal to edge. - public void DrawFlood(double[] ink, int x, int y, out int left, out int top, out int width, Image test = null, bool? equal = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(test), test); - options.AddIfPresent(nameof(equal), equal); - - options.Add("left", true); - options.Add("top", true); - options.Add("width", true); - - var results = this.Call("draw_flood", options, ink, x, y) as object[]; - - var opts = results?[1] as VOption; - left = opts?["left"] is int out1 ? out1 : 0; - top = opts?["top"] is int out2 ? out2 : 0; - width = opts?["width"] is int out3 ? out3 : 0; - } - - /// - /// Flood-fill an area. - /// - /// - /// - /// image.Mutate(x => x.DrawFlood(ink, x, y, out var left, out var top, out var width, out var height, test: Image, equal: bool)); - /// - /// - /// Color for pixels. - /// DrawFlood start point. - /// DrawFlood start point. - /// Left edge of modified area. - /// Top edge of modified area. - /// Width of modified area. - /// Height of modified area. - /// Test pixels in this image. - /// DrawFlood while equal to edge. - public void DrawFlood(double[] ink, int x, int y, out int left, out int top, out int width, out int height, Image test = null, bool? equal = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(test), test); - options.AddIfPresent(nameof(equal), equal); - - options.Add("left", true); - options.Add("top", true); - options.Add("width", true); - options.Add("height", true); - - var results = this.Call("draw_flood", options, ink, x, y) as object[]; - - var opts = results?[1] as VOption; - left = opts?["left"] is int out1 ? out1 : 0; - top = opts?["top"] is int out2 ? out2 : 0; - width = opts?["width"] is int out3 ? out3 : 0; - height = opts?["height"] is int out4 ? out4 : 0; - } - - /// - /// Paint an image into another image. - /// - /// - /// - /// image.Mutate(x => x.DrawImage(sub, x, y, mode: Enums.CombineMode)); - /// - /// - /// Sub-image to insert into main image. - /// Draw image here. - /// Draw image here. - /// Combining mode. - public void DrawImage(Image sub, int x, int y, Enums.CombineMode? mode = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(mode), mode); - - this.Call("draw_image", options, sub, x, y); - } - - /// - /// Draw a line on an image. - /// - /// - /// - /// image.Mutate(x => x.DrawLine(ink, x1, y1, x2, y2)); - /// - /// - /// Color for pixels. - /// Start of draw_line. - /// Start of draw_line. - /// End of draw_line. - /// End of draw_line. - public void DrawLine(double[] ink, int x1, int y1, int x2, int y2) - { - this.Call("draw_line", ink, x1, y1, x2, y2); - } - - /// - /// Draw a mask on an image. - /// - /// - /// - /// image.Mutate(x => x.DrawMask(ink, mask, x, y)); - /// - /// - /// Color for pixels. - /// Mask of pixels to draw. - /// Draw mask here. - /// Draw mask here. - public void DrawMask(double[] ink, Image mask, int x, int y) - { - this.Call("draw_mask", ink, mask, x, y); - } - - /// - /// Paint a rectangle on an image. - /// - /// - /// - /// image.Mutate(x => x.DrawRect(ink, left, top, width, height, fill: bool)); - /// - /// - /// Color for pixels. - /// Rect to fill. - /// Rect to fill. - /// Rect to fill. - /// Rect to fill. - /// Draw a solid object. - public void DrawRect(double[] ink, int left, int top, int width, int height, bool? fill = null) - { - var options = new VOption(); - - options.AddIfPresent(nameof(fill), fill); - - this.Call("draw_rect", options, ink, left, top, width, height); - } - - /// - /// Blur a rectangle on an image. - /// - /// - /// - /// image.Mutate(x => x.DrawSmudge(left, top, width, height)); - /// - /// - /// Rect to fill. - /// Rect to fill. - /// Rect to fill. - /// Rect to fill. - public void DrawSmudge(int left, int top, int width, int height) - { - this.Call("draw_smudge", left, top, width, height); - } - - #endregion + var options = new VOption(); + + options.AddIfPresent(nameof(fill), fill); + + this.Call("draw_rect", options, ink, left, top, width, height); } -} + + /// + /// Blur a rectangle on an image. + /// + /// + /// + /// image.Mutate(x => x.DrawSmudge(left, top, width, height)); + /// + /// + /// Rect to fill. + /// Rect to fill. + /// Rect to fill. + /// Rect to fill. + public void DrawSmudge(int left, int top, int width, int height) + { + this.Call("draw_smudge", left, top, width, height); + } + + #endregion +} \ No newline at end of file diff --git a/src/NetVips/MutableImage.cs b/src/NetVips/MutableImage.cs index cc44a804..5150397c 100644 --- a/src/NetVips/MutableImage.cs +++ b/src/NetVips/MutableImage.cs @@ -1,153 +1,153 @@ -namespace NetVips +using System; +using NetVips.Internal; + +using SMath = System.Math; + +namespace NetVips; + +/// +/// This class represents a libvips image which can be modified. See +/// . +/// +public sealed partial class MutableImage : Image { - using System; - using Internal; - using SMath = System.Math; + /// + /// The this is modifying. + /// Only use this once you have finished all modifications. + /// + internal Image Image { get; private set; } /// - /// This class represents a libvips image which can be modified. See - /// . + /// Make a from a regular copied . /// - public sealed partial class MutableImage : Image + /// + /// This is for internal use only. See for the + /// user-facing interface. + /// + internal MutableImage(Image copiedImage) : base(copiedImage.ObjectRef()) { - /// - /// The this is modifying. - /// Only use this once you have finished all modifications. - /// - internal Image Image { get; private set; } + Image = copiedImage; + } - /// - /// Make a from a regular copied . - /// - /// - /// This is for internal use only. See for the - /// user-facing interface. - /// - internal MutableImage(Image copiedImage) : base(copiedImage.ObjectRef()) - { - Image = copiedImage; - } + #region set/remove metadata - #region set/remove metadata + /// + /// Set the type and value of an item of metadata. + /// + /// + /// Sets the type and value of an item of metadata. Any old item of the + /// same name is removed. See for types. + /// + /// The GType of the metadata item to create. + /// The name of the piece of metadata to create. + /// The value to set as a C# value. It is + /// converted to the GType, if possible. + public new void Set(nint gtype, string name, object value) + { + using var gv = new GValue(); + gv.SetType(gtype); + gv.Set(value); + VipsImage.Set(this, name, in gv.Struct); + } - /// - /// Set the type and value of an item of metadata. - /// - /// - /// Sets the type and value of an item of metadata. Any old item of the - /// same name is removed. See for types. - /// - /// The GType of the metadata item to create. - /// The name of the piece of metadata to create. - /// The value to set as a C# value. It is - /// converted to the GType, if possible. - public new void Set(nint gtype, string name, object value) + /// + /// Set the value of an item of metadata. + /// + /// + /// Sets the value of an item of metadata. The metadata item must already + /// exist. + /// + /// The name of the piece of metadata to set the value of. + /// The value to set as a C# value. It is + /// converted to the type of the metadata item, if possible. + /// If metadata item does not exist. + public void Set(string name, object value) + { + var gtype = GetTypeOf(name); + if (gtype == IntPtr.Zero) { - using var gv = new GValue(); - gv.SetType(gtype); - gv.Set(value); - VipsImage.Set(this, name, in gv.Struct); + throw new ArgumentException( + $"metadata item {name} does not exist - use the Set(nint, string, object) overload to create and set"); } - /// - /// Set the value of an item of metadata. - /// - /// - /// Sets the value of an item of metadata. The metadata item must already - /// exist. - /// - /// The name of the piece of metadata to set the value of. - /// The value to set as a C# value. It is - /// converted to the type of the metadata item, if possible. - /// If metadata item does not exist. - public void Set(string name, object value) - { - var gtype = GetTypeOf(name); - if (gtype == IntPtr.Zero) - { - throw new ArgumentException( - $"metadata item {name} does not exist - use the Set(nint, string, object) overload to create and set"); - } - - Set(gtype, name, value); - } + Set(gtype, name, value); + } - /// - /// Remove an item of metadata. - /// - /// - /// The named metadata item is removed. - /// - /// The name of the piece of metadata to remove. - /// if the metadata is successfully removed; - /// otherwise, . - public bool Remove(string name) - { - return VipsImage.Remove(this, name); - } + /// + /// Remove an item of metadata. + /// + /// + /// The named metadata item is removed. + /// + /// The name of the piece of metadata to remove. + /// if the metadata is successfully removed; + /// otherwise, . + public bool Remove(string name) + { + return VipsImage.Remove(this, name); + } - #endregion + #endregion - #region overrides + #region overrides - /// - /// Overload `[]`. - /// - /// - /// Use `[]` to set band elements on an image. For example: - /// - /// using var test = image.Mutate(x => x[1] = green); - /// - /// Will change band 1 (the middle band). - /// - /// The band element to change. - public new Image this[int i] + /// + /// Overload `[]`. + /// + /// + /// Use `[]` to set band elements on an image. For example: + /// + /// using var test = image.Mutate(x => x[1] = green); + /// + /// Will change band 1 (the middle band). + /// + /// The band element to change. + public new Image this[int i] + { + set { - set + // number of bands to the left and right of value + var nLeft = SMath.Min(Bands, SMath.Max(0, i)); + var nRight = SMath.Min(Bands, SMath.Max(0, Bands - 1 - i)); + var offset = Bands - nRight; + using var left = nLeft > 0 ? Image.ExtractBand(0, n: nLeft) : null; + using var right = nRight > 0 ? Image.ExtractBand(offset, n: nRight) : null; + if (left == null) { - // number of bands to the left and right of value - var nLeft = SMath.Min(Bands, SMath.Max(0, i)); - var nRight = SMath.Min(Bands, SMath.Max(0, Bands - 1 - i)); - var offset = Bands - nRight; - using var left = nLeft > 0 ? Image.ExtractBand(0, n: nLeft) : null; - using var right = nRight > 0 ? Image.ExtractBand(offset, n: nRight) : null; - if (left == null) + using (Image) { - using (Image) - { - Image = value.Bandjoin(right); - } + Image = value.Bandjoin(right); } - else if (right == null) + } + else if (right == null) + { + using (Image) { - using (Image) - { - Image = left.Bandjoin(value); - } + Image = left.Bandjoin(value); } - else + } + else + { + using (Image) { - using (Image) - { - Image = left.Bandjoin(value, right); - } + Image = left.Bandjoin(value, right); } } } + } - /// - public override Image Mutate(Action action) - { - action.Invoke(this); - return Image; - } - - /// - public override string ToString() - { - return $""; - } + /// + public override Image Mutate(Action action) + { + action.Invoke(this); + return Image; + } - #endregion + /// + public override string ToString() + { + return $""; } + + #endregion } \ No newline at end of file diff --git a/src/NetVips/NetVips.cs b/src/NetVips/NetVips.cs index d69fada3..27f6edfd 100644 --- a/src/NetVips/NetVips.cs +++ b/src/NetVips/NetVips.cs @@ -1,555 +1,554 @@ -namespace NetVips +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using NetVips.Internal; + +namespace NetVips; + +/// +/// Basic utility stuff. +/// +public static class NetVips { - using System; - using System.Linq; - using System.Collections.Generic; - using System.Runtime.InteropServices; - using Internal; - - /// - /// Basic utility stuff. - /// - public static class NetVips - { - /// - /// Init() starts up the world of VIPS. - /// - /// - /// This function will be automatically called by - /// once the assembly is loaded. You should only call this method in your own program if the - /// fails to initialize libvips. - /// - /// if successful started; otherwise, . - public static bool Init() - { - return Vips.Init("NetVips") == 0; - } + /// + /// Init() starts up the world of VIPS. + /// + /// + /// This function will be automatically called by + /// once the assembly is loaded. You should only call this method in your own program if the + /// fails to initialize libvips. + /// + /// if successful started; otherwise, . + public static bool Init() + { + return Vips.Init("NetVips") == 0; + } - /// - /// Call this to drop caches, close plugins, terminate background threads, and finalize - /// any internal library testing. - /// - /// - /// Calling this is optional. If you don't call it, your platform will clean up for you. - /// The only negative consequences are that the leak checker () - /// and the profiler () will not work. - /// - public static void Shutdown() - { - Vips.Shutdown(); - } + /// + /// Call this to drop caches, close plugins, terminate background threads, and finalize + /// any internal library testing. + /// + /// + /// Calling this is optional. If you don't call it, your platform will clean up for you. + /// The only negative consequences are that the leak checker () + /// and the profiler () will not work. + /// + public static void Shutdown() + { + Vips.Shutdown(); + } - /// - /// Enable or disable libvips leak checking. - /// - /// - /// With this enabled, libvips will check for object and area leaks on . - /// Enabling this option will make libvips run slightly more slowly. - /// - /// Bool indicating if leak checking should be turned on. - [Obsolete("NetVips.LeakSet is deprecated, please use the NetVips.Leak setter instead.")] - public static void LeakSet(bool leak) - { - Leak = leak; - } + /// + /// Enable or disable libvips leak checking. + /// + /// + /// With this enabled, libvips will check for object and area leaks on . + /// Enabling this option will make libvips run slightly more slowly. + /// + /// Bool indicating if leak checking should be turned on. + [Obsolete("NetVips.LeakSet is deprecated, please use the NetVips.Leak setter instead.")] + public static void LeakSet(bool leak) + { + Leak = leak; + } - /// - /// Enable or disable libvips leak checking. - /// - /// - /// With this enabled, libvips will check for object and area leaks on . - /// Enabling this option will make libvips run slightly more slowly. - /// - public static bool Leak - { - set => Vips.LeakSet(value); - } + /// + /// Enable or disable libvips leak checking. + /// + /// + /// With this enabled, libvips will check for object and area leaks on . + /// Enabling this option will make libvips run slightly more slowly. + /// + public static bool Leak + { + set => Vips.LeakSet(value); + } - /// - /// Enable or disable libvips profile recording. - /// - /// - /// If set, vips will record profiling information, and dump it on . - /// These profiles can be analyzed with the `vipsprofile` program. - /// - /// Bool indicating if profile recording should be turned on. - [Obsolete("NetVips.ProfileSet is deprecated, please use the NetVips.Profile setter instead.")] - public static void ProfileSet(bool profile) - { - Profile = profile; - } + /// + /// Enable or disable libvips profile recording. + /// + /// + /// If set, vips will record profiling information, and dump it on . + /// These profiles can be analyzed with the `vipsprofile` program. + /// + /// Bool indicating if profile recording should be turned on. + [Obsolete("NetVips.ProfileSet is deprecated, please use the NetVips.Profile setter instead.")] + public static void ProfileSet(bool profile) + { + Profile = profile; + } - /// - /// Enable or disable libvips profile recording. - /// - /// - /// If set, vips will record profiling information, and dump it on . - /// These profiles can be analyzed with the `vipsprofile` program. - /// - public static bool Profile - { - set => Vips.ProfileSet(value); - } + /// + /// Enable or disable libvips profile recording. + /// + /// + /// If set, vips will record profiling information, and dump it on . + /// These profiles can be analyzed with the `vipsprofile` program. + /// + public static bool Profile + { + set => Vips.ProfileSet(value); + } - /// - /// Set the maximum number of operations libvips will cache. - /// - /// Maximum number of operations. - [Obsolete("NetVips.CacheSetMax is deprecated, please use the Cache.Max setter instead.")] - public static void CacheSetMax(int max) - { - Cache.Max = max; - } + /// + /// Set the maximum number of operations libvips will cache. + /// + /// Maximum number of operations. + [Obsolete("NetVips.CacheSetMax is deprecated, please use the Cache.Max setter instead.")] + public static void CacheSetMax(int max) + { + Cache.Max = max; + } - /// - /// Limit the operation cache by memory use. - /// - /// Maximum memory use. - [Obsolete("NetVips.CacheSetMaxMem is deprecated, please use the Cache.MaxMem setter instead.")] - public static void CacheSetMaxMem(ulong maxMem) - { - Cache.MaxMem = maxMem; - } + /// + /// Limit the operation cache by memory use. + /// + /// Maximum memory use. + [Obsolete("NetVips.CacheSetMaxMem is deprecated, please use the Cache.MaxMem setter instead.")] + public static void CacheSetMaxMem(ulong maxMem) + { + Cache.MaxMem = maxMem; + } - /// - /// Limit the operation cache by number of open files. - /// - /// Maximum open files. - [Obsolete("NetVips.CacheSetMaxFiles is deprecated, please use the Cache.MaxFiles setter instead.")] - public static void CacheSetMaxFiles(int maxFiles) - { - Cache.MaxFiles = maxFiles; - } + /// + /// Limit the operation cache by number of open files. + /// + /// Maximum open files. + [Obsolete("NetVips.CacheSetMaxFiles is deprecated, please use the Cache.MaxFiles setter instead.")] + public static void CacheSetMaxFiles(int maxFiles) + { + Cache.MaxFiles = maxFiles; + } - /// - /// Turn on libvips cache tracing. - /// - /// Bool indicating if tracing should be turned on. - [Obsolete("NetVips.CacheSetTrace is deprecated, please use the Cache.Trace setter instead.")] - public static void CacheSetTrace(bool trace) - { - Cache.Trace = trace; - } + /// + /// Turn on libvips cache tracing. + /// + /// Bool indicating if tracing should be turned on. + [Obsolete("NetVips.CacheSetTrace is deprecated, please use the Cache.Trace setter instead.")] + public static void CacheSetTrace(bool trace) + { + Cache.Trace = trace; + } - /// - /// Set the size of the pools of worker threads vips uses for image evaluation. - /// - /// The size of the pools of worker threads vips uses - /// for image evaluation. - [Obsolete("NetVips.ConcurrencySet is deprecated, please use the NetVips.Concurrency setter instead.")] - public static void ConcurrencySet(int concurrency) - { - Concurrency = concurrency; - } + /// + /// Set the size of the pools of worker threads vips uses for image evaluation. + /// + /// The size of the pools of worker threads vips uses + /// for image evaluation. + [Obsolete("NetVips.ConcurrencySet is deprecated, please use the NetVips.Concurrency setter instead.")] + public static void ConcurrencySet(int concurrency) + { + Concurrency = concurrency; + } - /// - /// Returns the number of worker threads that vips uses for image evaluation. - /// - /// The number of worker threads. - [Obsolete("NetVips.ConcurrencyGet is deprecated, please use the NetVips.Concurrency getter instead.")] - public static int ConcurrencyGet() - { - return Concurrency; - } + /// + /// Returns the number of worker threads that vips uses for image evaluation. + /// + /// The number of worker threads. + [Obsolete("NetVips.ConcurrencyGet is deprecated, please use the NetVips.Concurrency getter instead.")] + public static int ConcurrencyGet() + { + return Concurrency; + } - /// - /// Gets or sets the number of worker threads libvips' should create to process each image. - /// - public static int Concurrency - { - get => Vips.ConcurrencyGet(); - set => Vips.ConcurrencySet(value); - } + /// + /// Gets or sets the number of worker threads libvips' should create to process each image. + /// + public static int Concurrency + { + get => Vips.ConcurrencyGet(); + set => Vips.ConcurrencySet(value); + } - /// - /// Enable or disable SIMD. - /// - /// Bool indicating if SIMD should be turned on. - [Obsolete("NetVips.VectorSet is deprecated, please use the NetVips.Vector setter instead.")] - public static void VectorSet(bool enabled) - { - Vector = enabled; - } + /// + /// Enable or disable SIMD. + /// + /// Bool indicating if SIMD should be turned on. + [Obsolete("NetVips.VectorSet is deprecated, please use the NetVips.Vector setter instead.")] + public static void VectorSet(bool enabled) + { + Vector = enabled; + } - /// - /// Enable or disable SIMD. - /// - public static bool Vector - { - get => Vips.VectorIsEnabled(); - set => Vips.VectorSet(value); - } + /// + /// Enable or disable SIMD. + /// + public static bool Vector + { + get => Vips.VectorIsEnabled(); + set => Vips.VectorSet(value); + } - /// - /// Set the block state on all untrusted operations. - /// - /// - /// For example: - /// - /// NetVips.BlockUntrusted = true; - /// - /// Will block all untrusted operations from running. Use: - /// - /// $ vips -l - /// - /// at the command-line to see the class hierarchy and which - /// operations are marked as untrusted. Use - /// to set the block state on - /// specific operations in the libvips class hierarchy. - /// - /// At least libvips 8.13 is needed. - /// - public static bool BlockUntrusted - { - set => Vips.BlockUntrustedSet(value); - } + /// + /// Set the block state on all untrusted operations. + /// + /// + /// For example: + /// + /// NetVips.BlockUntrusted = true; + /// + /// Will block all untrusted operations from running. Use: + /// + /// $ vips -l + /// + /// at the command-line to see the class hierarchy and which + /// operations are marked as untrusted. Use + /// to set the block state on + /// specific operations in the libvips class hierarchy. + /// + /// At least libvips 8.13 is needed. + /// + public static bool BlockUntrusted + { + set => Vips.BlockUntrustedSet(value); + } - /// - /// Returns an array with: - /// - the number of active allocations. - /// - the number of bytes currently allocated via `vips_malloc()` and friends. - /// - the number of open files. - /// - /// An array with memory stats. Handy for debugging / leak testing. - [Obsolete("NetVips.MemoryStats is deprecated, please use the Stats class instead.")] - public static int[] MemoryStats() + /// + /// Returns an array with: + /// - the number of active allocations. + /// - the number of bytes currently allocated via `vips_malloc()` and friends. + /// - the number of open files. + /// + /// An array with memory stats. Handy for debugging / leak testing. + [Obsolete("NetVips.MemoryStats is deprecated, please use the Stats class instead.")] + public static int[] MemoryStats() + { + return new[] { - return new[] - { - Stats.Allocations, - Stats.Mem, - Stats.Files - }; - } + Stats.Allocations, + Stats.Mem, + Stats.Files + }; + } - /// - /// Returns the largest number of bytes simultaneously allocated via vips_tracked_malloc(). - /// Handy for estimating max memory requirements for a program. - /// - /// The largest number of bytes simultaneously allocated. - [Obsolete("NetVips.MemoryHigh is deprecated, please use the Stats.MemHighwater getter instead.")] - public static ulong MemoryHigh() - { - return Stats.MemHighwater; - } + /// + /// Returns the largest number of bytes simultaneously allocated via vips_tracked_malloc(). + /// Handy for estimating max memory requirements for a program. + /// + /// The largest number of bytes simultaneously allocated. + [Obsolete("NetVips.MemoryHigh is deprecated, please use the Stats.MemHighwater getter instead.")] + public static ulong MemoryHigh() + { + return Stats.MemHighwater; + } - /// - /// Get the major, minor or patch version number of the libvips library. - /// - /// Pass 0 to get the major version number, 1 to get minor, 2 to get patch. - /// to get this value from the pre-initialized - /// variable. - /// The version number. - /// If is not in range. - public static int Version(int flag, bool fromModule = true) + /// + /// Get the major, minor or patch version number of the libvips library. + /// + /// Pass 0 to get the major version number, 1 to get minor, 2 to get patch. + /// to get this value from the pre-initialized + /// variable. + /// The version number. + /// If is not in range. + public static int Version(int flag, bool fromModule = true) + { + if (fromModule && ModuleInitializer.Version.HasValue) { - if (fromModule && ModuleInitializer.Version.HasValue) - { - var version = ModuleInitializer.Version.Value; - switch (flag) - { - case 0: - return (version >> 16) & 0xFF; - case 1: - return (version >> 8) & 0xFF; - case 2: - return version & 0xFF; - } - } - - if (flag < 0 || flag > 2) + var version = ModuleInitializer.Version.Value; + switch (flag) { - throw new ArgumentOutOfRangeException(nameof(flag), "Flag must be in the range of 0 to 2"); + case 0: + return (version >> 16) & 0xFF; + case 1: + return (version >> 8) & 0xFF; + case 2: + return version & 0xFF; } - - var value = Vips.Version(flag); - if (value < 0) - { - throw new VipsException("Unable to get library version"); - } - - return value; } - /// - /// Is this at least libvips major.minor[.patch]? - /// - /// Major component. - /// Minor component. - /// Patch component. - /// if at least libvips major.minor[.patch]; otherwise, . - public static bool AtLeastLibvips(int x, int y, int z = 0) + if (flag < 0 || flag > 2) { - var major = Version(0); - var minor = Version(1); - var patch = Version(2); - - return major > x || - major == x && minor > y || - major == x && minor == y && patch >= z; + throw new ArgumentOutOfRangeException(nameof(flag), "Flag must be in the range of 0 to 2"); } - /// - /// Get a list of all the filename suffixes supported by libvips. - /// - /// - /// At least libvips 8.8 is needed. - /// - /// An array of strings or . - public static string[] GetSuffixes() + var value = Vips.Version(flag); + if (value < 0) { - if (!AtLeastLibvips(8, 8)) - { - return null; - } - - var ptrArr = VipsForeign.GetSuffixes(); - - var names = new List(); + throw new VipsException("Unable to get library version"); + } - var count = 0; - nint strPtr; - while ((strPtr = Marshal.ReadIntPtr(ptrArr, count * IntPtr.Size)) != IntPtr.Zero) - { - var name = Marshal.PtrToStringAnsi(strPtr); - names.Add(name); - GLib.GFree(strPtr); - ++count; - } + return value; + } - GLib.GFree(ptrArr); + /// + /// Is this at least libvips major.minor[.patch]? + /// + /// Major component. + /// Minor component. + /// Patch component. + /// if at least libvips major.minor[.patch]; otherwise, . + public static bool AtLeastLibvips(int x, int y, int z = 0) + { + var major = Version(0); + var minor = Version(1); + var patch = Version(2); - return names.ToArray(); - } + return major > x || + major == x && minor > y || + major == x && minor == y && patch >= z; + } - /// - /// Reports leaks (hopefully there are none) it also tracks and reports peak memory use. - /// - internal static void ReportLeak() + /// + /// Get a list of all the filename suffixes supported by libvips. + /// + /// + /// At least libvips 8.8 is needed. + /// + /// An array of strings or . + public static string[] GetSuffixes() + { + if (!AtLeastLibvips(8, 8)) { - VipsObject.PrintAll(); + return null; + } - Console.WriteLine("memory: {0} allocations, {1} bytes", Stats.Allocations, Stats.Mem); - Console.WriteLine("files: {0} open", Stats.Files); + var ptrArr = VipsForeign.GetSuffixes(); - Console.WriteLine("memory: high-water mark: {0}", Stats.MemHighwater.ToReadableBytes()); + var names = new List(); - var errorBuffer = Marshal.PtrToStringAnsi(Vips.ErrorBuffer()); - if (!string.IsNullOrEmpty(errorBuffer)) - { - Console.WriteLine("error buffer: {0}", errorBuffer); - } + var count = 0; + nint strPtr; + while ((strPtr = Marshal.ReadIntPtr(ptrArr, count * IntPtr.Size)) != IntPtr.Zero) + { + var name = Marshal.PtrToStringAnsi(strPtr); + names.Add(name); + GLib.GFree(strPtr); + ++count; } - #region unit test functions + GLib.GFree(ptrArr); - /// - /// For testing only. - /// - /// Path to split. - /// The filename part of a vips7 path. - internal static string PathFilename7(string path) - { - return Vips.PathFilename7(path); - } + return names.ToArray(); + } - /// - /// For testing only. - /// - /// Path to split. - /// The mode part of a vips7 path. - internal static string PathMode7(string path) - { - return Vips.PathMode7(path); - } + /// + /// Reports leaks (hopefully there are none) it also tracks and reports peak memory use. + /// + internal static void ReportLeak() + { + VipsObject.PrintAll(); - /// - /// For testing only. - /// - internal static void VipsInterpretationGetType() - { - Vips.InterpretationGetType(); - } + Console.WriteLine("memory: {0} allocations, {1} bytes", Stats.Allocations, Stats.Mem); + Console.WriteLine("files: {0} open", Stats.Files); - /// - /// For testing only. - /// - internal static void VipsOperationFlagsGetType() - { - VipsOperation.FlagsGetType(); - } + Console.WriteLine("memory: high-water mark: {0}", Stats.MemHighwater.ToReadableBytes()); - #endregion - - /// - /// Get the GType for a name. - /// - /// - /// Looks up the GType for a nickname. Types below basename in the type - /// hierarchy are searched. - /// - /// Name of base class. - /// Search for a class with this nickname. - /// The GType of the class, or if the class is not found. - public static nint TypeFind(string basename, string nickname) + var errorBuffer = Marshal.PtrToStringAnsi(Vips.ErrorBuffer()); + if (!string.IsNullOrEmpty(errorBuffer)) { - return Vips.TypeFind(basename, nickname); + Console.WriteLine("error buffer: {0}", errorBuffer); } + } - /// - /// Return the name for a GType. - /// - /// Type to return name for. - /// Type name. - public static string TypeName(nint type) - { - return Marshal.PtrToStringAnsi(GType.Name(type)); - } + #region unit test functions - /// - /// Return the nickname for a GType. - /// - /// Type to return nickname for. - /// Nickname. - public static string NicknameFind(nint type) - { - return Marshal.PtrToStringAnsi(Vips.NicknameFind(type)); - } + /// + /// For testing only. + /// + /// Path to split. + /// The filename part of a vips7 path. + internal static string PathFilename7(string path) + { + return Vips.PathFilename7(path); + } - /// - /// Get a list of operations available within the libvips library. - /// - /// - /// This can be useful for documentation generators. - /// - /// A list of operations. - public static List GetOperations() - { - var allNickNames = new List(); - var handle = GCHandle.Alloc(allNickNames); + /// + /// For testing only. + /// + /// Path to split. + /// The mode part of a vips7 path. + internal static string PathMode7(string path) + { + return Vips.PathMode7(path); + } - nint TypeMap(nint type, nint a, nint b) - { - var nickname = NicknameFind(type); + /// + /// For testing only. + /// + internal static void VipsInterpretationGetType() + { + Vips.InterpretationGetType(); + } - // exclude base classes, for e.g. 'jpegload_base' - if (TypeFind("VipsOperation", nickname) != IntPtr.Zero) - { - var list = (List)GCHandle.FromIntPtr(a).Target; - list.Add(NicknameFind(type)); - } + /// + /// For testing only. + /// + internal static void VipsOperationFlagsGetType() + { + VipsOperation.FlagsGetType(); + } - return Vips.TypeMap(type, TypeMap, a, b); - } + #endregion - try - { - Vips.TypeMap(TypeFromName("VipsOperation"), TypeMap, GCHandle.ToIntPtr(handle), IntPtr.Zero); - } - finally - { - handle.Free(); - } + /// + /// Get the GType for a name. + /// + /// + /// Looks up the GType for a nickname. Types below basename in the type + /// hierarchy are searched. + /// + /// Name of base class. + /// Search for a class with this nickname. + /// The GType of the class, or if the class is not found. + public static nint TypeFind(string basename, string nickname) + { + return Vips.TypeFind(basename, nickname); + } - // Sort - allNickNames.Sort(); + /// + /// Return the name for a GType. + /// + /// Type to return name for. + /// Type name. + public static string TypeName(nint type) + { + return Marshal.PtrToStringAnsi(GType.Name(type)); + } - // Filter duplicates - allNickNames = allNickNames.Distinct().ToList(); + /// + /// Return the nickname for a GType. + /// + /// Type to return nickname for. + /// Nickname. + public static string NicknameFind(nint type) + { + return Marshal.PtrToStringAnsi(Vips.NicknameFind(type)); + } - return allNickNames; - } + /// + /// Get a list of operations available within the libvips library. + /// + /// + /// This can be useful for documentation generators. + /// + /// A list of operations. + public static List GetOperations() + { + var allNickNames = new List(); + var handle = GCHandle.Alloc(allNickNames); - /// - /// Get a list of enums available within the libvips library. - /// - /// A list of enums. - public static List GetEnums() + nint TypeMap(nint type, nint a, nint b) { - var allEnums = new List(); - var handle = GCHandle.Alloc(allEnums); + var nickname = NicknameFind(type); - nint TypeMap(nint type, nint a, nint b) + // exclude base classes, for e.g. 'jpegload_base' + if (TypeFind("VipsOperation", nickname) != IntPtr.Zero) { - var nickname = TypeName(type); - var list = (List)GCHandle.FromIntPtr(a).Target; - list.Add(nickname); - - return Vips.TypeMap(type, TypeMap, a, b); + list.Add(NicknameFind(type)); } - try - { - Vips.TypeMap(TypeFromName("GEnum"), TypeMap, GCHandle.ToIntPtr(handle), IntPtr.Zero); - } - finally - { - handle.Free(); - } - - // Sort - allEnums.Sort(); - - return allEnums; + return Vips.TypeMap(type, TypeMap, a, b); } - /// - /// Get all values for a enum (GType). - /// - /// Type to return enum values for. - /// A list of values. - public static Dictionary ValuesForEnum(nint type) + try + { + Vips.TypeMap(TypeFromName("VipsOperation"), TypeMap, GCHandle.ToIntPtr(handle), IntPtr.Zero); + } + finally { - var typeClass = GType.ClassRef(type); - var enumClass = Marshal.PtrToStructure(typeClass); + handle.Free(); + } - var values = new Dictionary((int)(enumClass.NValues - 1)); + // Sort + allNickNames.Sort(); - var ptr = enumClass.Values; + // Filter duplicates + allNickNames = allNickNames.Distinct().ToList(); - // -1 since we always have a "last" member - for (var i = 0; i < enumClass.NValues - 1; i++) - { - var enumValue = Marshal.PtrToStructure(ptr); - values[enumValue.ValueNick] = enumValue.Value; + return allNickNames; + } - ptr += Marshal.SizeOf(); - } + /// + /// Get a list of enums available within the libvips library. + /// + /// A list of enums. + public static List GetEnums() + { + var allEnums = new List(); + var handle = GCHandle.Alloc(allEnums); - return values; + nint TypeMap(nint type, nint a, nint b) + { + var nickname = TypeName(type); + + var list = (List)GCHandle.FromIntPtr(a).Target; + list.Add(nickname); + + return Vips.TypeMap(type, TypeMap, a, b); } - /// - /// Return the GType for a name. - /// - /// Type name to lookup. - /// Corresponding type ID or . - public static nint TypeFromName(string name) + try { - return GType.FromName(name); + Vips.TypeMap(TypeFromName("GEnum"), TypeMap, GCHandle.ToIntPtr(handle), IntPtr.Zero); } - - /// - /// Extract the fundamental type ID portion. - /// - /// A valid type ID. - /// Fundamental type ID. - public static nint FundamentalType(nint type) + finally { - return GType.Fundamental(type); + handle.Free(); } - /// - /// Frees the memory pointed to by . - /// - /// - /// This is needed for . - /// - /// The memory to free. - public static void Free(nint mem) + // Sort + allEnums.Sort(); + + return allEnums; + } + + /// + /// Get all values for a enum (GType). + /// + /// Type to return enum values for. + /// A list of values. + public static Dictionary ValuesForEnum(nint type) + { + var typeClass = GType.ClassRef(type); + var enumClass = Marshal.PtrToStructure(typeClass); + + var values = new Dictionary((int)(enumClass.NValues - 1)); + + var ptr = enumClass.Values; + + // -1 since we always have a "last" member + for (var i = 0; i < enumClass.NValues - 1; i++) { - GLib.GFree(mem); + var enumValue = Marshal.PtrToStructure(ptr); + values[enumValue.ValueNick] = enumValue.Value; + + ptr += Marshal.SizeOf(); } + + return values; + } + + /// + /// Return the GType for a name. + /// + /// Type name to lookup. + /// Corresponding type ID or . + public static nint TypeFromName(string name) + { + return GType.FromName(name); + } + + /// + /// Extract the fundamental type ID portion. + /// + /// A valid type ID. + /// Fundamental type ID. + public static nint FundamentalType(nint type) + { + return GType.Fundamental(type); + } + + /// + /// Frees the memory pointed to by . + /// + /// + /// This is needed for . + /// + /// The memory to free. + public static void Free(nint mem) + { + GLib.GFree(mem); } } \ No newline at end of file diff --git a/src/NetVips/Operation.cs b/src/NetVips/Operation.cs index d7778618..35ea4215 100644 --- a/src/NetVips/Operation.cs +++ b/src/NetVips/Operation.cs @@ -1,272 +1,271 @@ -namespace NetVips +using System; +using NetVips.Internal; + +namespace NetVips; + +/// +/// Wrap a object. +/// +public class Operation : VipsObject { - using System; - using Internal; + /// + private Operation(IntPtr pointer) + : base(pointer) + { + } /// - /// Wrap a object. + /// Create a new with the specified nickname. /// - public class Operation : VipsObject + /// + /// You'll need to set any arguments and build the operation before you can use it. See + /// for a higher-level way to make new operations. + /// + /// Nickname of operation to create. + /// The new operation. + /// If the operation doesn't exist. + public static Operation NewFromName(string operationName) { - /// - private Operation(IntPtr pointer) - : base(pointer) + var vop = VipsOperation.New(operationName); + if (vop == IntPtr.Zero) { + throw new VipsException($"no such operation {operationName}"); } - /// - /// Create a new with the specified nickname. - /// - /// - /// You'll need to set any arguments and build the operation before you can use it. See - /// for a higher-level way to make new operations. - /// - /// Nickname of operation to create. - /// The new operation. - /// If the operation doesn't exist. - public static Operation NewFromName(string operationName) + return new Operation(vop); + } + + /// + /// Set a GObject property. The value is converted to the property type, if possible. + /// + /// The name of the property to set. + /// A used as guide. + /// The value. + /// The GType of the property. + private void Set(nint gtype, Image matchImage, string name, object value) + { + // if the object wants an image and we have a constant, Imageize it + // + // if the object wants an image array, Imageize any constants in the + // array + if (matchImage != null) { - var vop = VipsOperation.New(operationName); - if (vop == IntPtr.Zero) + if (gtype == GValue.ImageType) { - throw new VipsException($"no such operation {operationName}"); + value = Image.Imageize(matchImage, value); } - - return new Operation(vop); - } - - /// - /// Set a GObject property. The value is converted to the property type, if possible. - /// - /// The name of the property to set. - /// A used as guide. - /// The value. - /// The GType of the property. - private void Set(nint gtype, Image matchImage, string name, object value) - { - // if the object wants an image and we have a constant, Imageize it - // - // if the object wants an image array, Imageize any constants in the - // array - if (matchImage != null) + else if (gtype == GValue.ArrayImageType) { - if (gtype == GValue.ImageType) + if (value is not Array { Rank: 1 } values) { - value = Image.Imageize(matchImage, value); + throw new ArgumentException( + $"unsupported value type {value.GetType()} for VipsArrayImage"); } - else if (gtype == GValue.ArrayImageType) - { - if (value is not Array { Rank: 1 } values) - { - throw new ArgumentException( - $"unsupported value type {value.GetType()} for VipsArrayImage"); - } - - var images = new Image[values.Length]; - for (var i = 0; i < values.Length; i++) - { - ref var image = ref images[i]; - image = Image.Imageize(matchImage, values.GetValue(i)); - } - value = images; + var images = new Image[values.Length]; + for (var i = 0; i < values.Length; i++) + { + ref var image = ref images[i]; + image = Image.Imageize(matchImage, values.GetValue(i)); } - } - Set(gtype, name, value); + value = images; + } } - /// - /// Lookup the set of flags for this operation. - /// - /// Flags for this operation. - public Enums.OperationFlags GetFlags() => VipsOperation.GetFlags(this); + Set(gtype, name, value); + } + + /// + /// Lookup the set of flags for this operation. + /// + /// Flags for this operation. + public Enums.OperationFlags GetFlags() => VipsOperation.GetFlags(this); + + /// + /// Call a libvips operation. + /// + /// + /// Use this method to call any libvips operation. For example: + /// + /// using Image blackImage = Operation.Call("black", 10, 10) as Image; + /// + /// See the Introduction for notes on how this works. + /// + /// Operation name. + /// An arbitrary number and variety of arguments. + /// A new object. + public static object Call(string operationName, params object[] args) => + Call(operationName, null, null, args); + + /// + /// Call a libvips operation. + /// + /// + /// Use this method to call any libvips operation. For example: + /// + /// using Image blackImage = Operation.Call("black", 10, 10) as Image; + /// + /// See the Introduction for notes on how this works. + /// + /// Operation name. + /// Optional arguments. + /// An arbitrary number and variety of arguments. + /// A new object. + public static object Call(string operationName, VOption kwargs = null, params object[] args) => + Call(operationName, kwargs, null, args); - /// - /// Call a libvips operation. - /// - /// - /// Use this method to call any libvips operation. For example: - /// - /// using Image blackImage = Operation.Call("black", 10, 10) as Image; - /// - /// See the Introduction for notes on how this works. - /// - /// Operation name. - /// An arbitrary number and variety of arguments. - /// A new object. - public static object Call(string operationName, params object[] args) => - Call(operationName, null, null, args); + /// + /// Call a libvips operation. + /// + /// + /// Use this method to call any libvips operation. For example: + /// + /// using Image blackImage = Operation.Call("black", 10, 10) as Image; + /// + /// See the Introduction for notes on how this works. + /// + /// Operation name. + /// Optional arguments. + /// A used as guide. + /// An arbitrary number and variety of arguments. + /// A new object. + public static object Call(string operationName, VOption kwargs = null, Image matchImage = null, + params object[] args) + { + // pull out the special string_options kwarg + object stringOptions = null; + kwargs?.Remove("string_options", out stringOptions); - /// - /// Call a libvips operation. - /// - /// - /// Use this method to call any libvips operation. For example: - /// - /// using Image blackImage = Operation.Call("black", 10, 10) as Image; - /// - /// See the Introduction for notes on how this works. - /// - /// Operation name. - /// Optional arguments. - /// An arbitrary number and variety of arguments. - /// A new object. - public static object Call(string operationName, VOption kwargs = null, params object[] args) => - Call(operationName, kwargs, null, args); + var intro = Introspect.Get(operationName); + if (intro.RequiredInput.Count != args.Length) + { + throw new ArgumentException( + $"unable to call {operationName}: {args.Length} arguments given, but {intro.RequiredInput.Count} required"); + } - /// - /// Call a libvips operation. - /// - /// - /// Use this method to call any libvips operation. For example: - /// - /// using Image blackImage = Operation.Call("black", 10, 10) as Image; - /// - /// See the Introduction for notes on how this works. - /// - /// Operation name. - /// Optional arguments. - /// A used as guide. - /// An arbitrary number and variety of arguments. - /// A new object. - public static object Call(string operationName, VOption kwargs = null, Image matchImage = null, - params object[] args) + if (!intro.Mutable && matchImage is MutableImage) { - // pull out the special string_options kwarg - object stringOptions = null; - kwargs?.Remove("string_options", out stringOptions); + throw new VipsException($"unable to call {operationName}: operation must be mutable"); + } - var intro = Introspect.Get(operationName); - if (intro.RequiredInput.Count != args.Length) + nint vop; + using (var op = NewFromName(operationName)) + { + // set any string options before any args so they can't be + // overridden + if (stringOptions != null && !op.SetString(stringOptions as string)) { - throw new ArgumentException( - $"unable to call {operationName}: {args.Length} arguments given, but {intro.RequiredInput.Count} required"); + throw new VipsException($"unable to call {operationName}"); } - if (!intro.Mutable && matchImage is MutableImage) + // set all required inputs + if (matchImage != null && intro.MemberX.HasValue) { - throw new VipsException($"unable to call {operationName}: operation must be mutable"); + var memberX = intro.MemberX.Value; + op.Set(memberX.Type, memberX.Name, matchImage); } - nint vop; - using (var op = NewFromName(operationName)) + for (var i = 0; i < intro.RequiredInput.Count; i++) { - // set any string options before any args so they can't be - // overridden - if (stringOptions != null && !op.SetString(stringOptions as string)) - { - throw new VipsException($"unable to call {operationName}"); - } - - // set all required inputs - if (matchImage != null && intro.MemberX.HasValue) - { - var memberX = intro.MemberX.Value; - op.Set(memberX.Type, memberX.Name, matchImage); - } + var arg = intro.RequiredInput[i]; + op.Set(arg.Type, matchImage, arg.Name, args[i]); + } - for (var i = 0; i < intro.RequiredInput.Count; i++) + // set all optional inputs, if any + if (kwargs != null) + { + foreach (var item in kwargs) { - var arg = intro.RequiredInput[i]; - op.Set(arg.Type, matchImage, arg.Name, args[i]); - } + var name = item.Key; + var value = item.Value; - // set all optional inputs, if any - if (kwargs != null) - { - foreach (var item in kwargs) + if (intro.OptionalInput.TryGetValue(name, out var arg)) { - var name = item.Key; - var value = item.Value; - - if (intro.OptionalInput.TryGetValue(name, out var arg)) - { - op.Set(arg.Type, matchImage, name, value); - } - else if (!intro.OptionalOutput.ContainsKey(name)) - { - throw new ArgumentException($"{operationName} does not support optional argument: {name}"); - } + op.Set(arg.Type, matchImage, name, value); + } + else if (!intro.OptionalOutput.ContainsKey(name)) + { + throw new ArgumentException($"{operationName} does not support optional argument: {name}"); } - } - - // build operation - vop = VipsOperation.Build(op); - if (vop == IntPtr.Zero) - { - Internal.VipsObject.UnrefOutputs(op); - throw new VipsException($"unable to call {operationName}"); } } - var results = new object[intro.RequiredOutput.Count]; - using (var op = new Operation(vop)) + // build operation + vop = VipsOperation.Build(op); + if (vop == IntPtr.Zero) { - // get all required results - for (var i = 0; i < intro.RequiredOutput.Count; i++) - { - var arg = intro.RequiredOutput[i]; + Internal.VipsObject.UnrefOutputs(op); + throw new VipsException($"unable to call {operationName}"); + } + } - ref var result = ref results[i]; - result = op.Get(arg.Name); - } + var results = new object[intro.RequiredOutput.Count]; + using (var op = new Operation(vop)) + { + // get all required results + for (var i = 0; i < intro.RequiredOutput.Count; i++) + { + var arg = intro.RequiredOutput[i]; - // fetch optional output args, if any - if (kwargs != null) - { - var optionalArgs = new VOption(); + ref var result = ref results[i]; + result = op.Get(arg.Name); + } - foreach (var item in kwargs) - { - var name = item.Key; + // fetch optional output args, if any + if (kwargs != null) + { + var optionalArgs = new VOption(); - if (intro.OptionalOutput.ContainsKey(name)) - { - optionalArgs[name] = op.Get(name); - } - } + foreach (var item in kwargs) + { + var name = item.Key; - if (optionalArgs.Count > 0) + if (intro.OptionalOutput.ContainsKey(name)) { - var resultsLength = results.Length; - Array.Resize(ref results, resultsLength + 1); - results[resultsLength] = optionalArgs; + optionalArgs[name] = op.Get(name); } } - Internal.VipsObject.UnrefOutputs(op); + if (optionalArgs.Count > 0) + { + var resultsLength = results.Length; + Array.Resize(ref results, resultsLength + 1); + results[resultsLength] = optionalArgs; + } } - return results.Length == 1 ? results[0] : results; + Internal.VipsObject.UnrefOutputs(op); } - /// - /// Set the block state on all operations in the libvips class hierarchy at - /// and below. - /// - /// - /// For example: - /// - /// Operation.Block("VipsForeignLoad", true); - /// Operation.Block("VipsForeignLoadJpeg", false); - /// - /// Will block all load operations, except JPEG. Use: - /// - /// $ vips -l - /// - /// at the command-line to see the class hierarchy. - /// Use to set the - /// block state on all untrusted operations. - /// - /// This call does nothing if the named operation is not found. - /// At least libvips 8.13 is needed. - /// - /// Set block state at this point and below. - /// The block state to set. - public static void Block(string name, bool state) - { - VipsOperation.BlockSet(name, state); - } + return results.Length == 1 ? results[0] : results; + } + + /// + /// Set the block state on all operations in the libvips class hierarchy at + /// and below. + /// + /// + /// For example: + /// + /// Operation.Block("VipsForeignLoad", true); + /// Operation.Block("VipsForeignLoadJpeg", false); + /// + /// Will block all load operations, except JPEG. Use: + /// + /// $ vips -l + /// + /// at the command-line to see the class hierarchy. + /// Use to set the + /// block state on all untrusted operations. + /// + /// This call does nothing if the named operation is not found. + /// At least libvips 8.13 is needed. + /// + /// Set block state at this point and below. + /// The block state to set. + public static void Block(string name, bool state) + { + VipsOperation.BlockSet(name, state); } } \ No newline at end of file diff --git a/src/NetVips/Region.cs b/src/NetVips/Region.cs index 47e6649f..72497a4d 100644 --- a/src/NetVips/Region.cs +++ b/src/NetVips/Region.cs @@ -1,73 +1,72 @@ -namespace NetVips +using System; +using System.Runtime.InteropServices; +using NetVips.Internal; + +namespace NetVips; + +/// +/// Wrap a object. +/// +/// +/// A region is a small part of an image. You use regions to read pixels +/// out of images without storing the entire image in memory. +/// At least libvips 8.8 is needed. +/// +public class Region : VipsObject { - using System; - using System.Runtime.InteropServices; - using Internal; + private Region(IntPtr pointer) + : base(pointer) + { + } /// - /// Wrap a object. + /// Make a region on an image. /// - /// - /// A region is a small part of an image. You use regions to read pixels - /// out of images without storing the entire image in memory. - /// At least libvips 8.8 is needed. - /// - public class Region : VipsObject + /// to create this region on. + /// A new . + /// If unable to make a new region on . + public static Region New(Image image) { - private Region(IntPtr pointer) - : base(pointer) + var vi = VipsRegion.New(image); + if (vi == IntPtr.Zero) { + throw new VipsException("unable to make region"); } - /// - /// Make a region on an image. - /// - /// to create this region on. - /// A new . - /// If unable to make a new region on . - public static Region New(Image image) - { - var vi = VipsRegion.New(image); - if (vi == IntPtr.Zero) - { - throw new VipsException("unable to make region"); - } - - return new Region(vi); - } + return new Region(vi); + } - /// - /// Width of pixels held by region. - /// - public int Width => VipsRegion.Width(this); + /// + /// Width of pixels held by region. + /// + public int Width => VipsRegion.Width(this); - /// - /// Height of pixels held by region. - /// - public int Height => VipsRegion.Height(this); + /// + /// Height of pixels held by region. + /// + public int Height => VipsRegion.Height(this); - /// - /// Fetch an area of pixels. - /// - /// Left edge of area to fetch. - /// Top edge of area to fetch. - /// Width of area to fetch. - /// Height of area to fetch. - /// An array of bytes filled with pixel data. - public byte[] Fetch(int left, int top, int width, int height) + /// + /// Fetch an area of pixels. + /// + /// Left edge of area to fetch. + /// Top edge of area to fetch. + /// Width of area to fetch. + /// Height of area to fetch. + /// An array of bytes filled with pixel data. + public byte[] Fetch(int left, int top, int width, int height) + { + var pointer = VipsRegion.Fetch(this, left, top, width, height, out var size); + if (pointer == IntPtr.Zero) { - var pointer = VipsRegion.Fetch(this, left, top, width, height, out var size); - if (pointer == IntPtr.Zero) - { - throw new VipsException("unable to fetch from region"); - } + throw new VipsException("unable to fetch from region"); + } - var managedArray = new byte[size]; - Marshal.Copy(pointer, managedArray, 0, (int)size); + var managedArray = new byte[size]; + Marshal.Copy(pointer, managedArray, 0, (int)size); - GLib.GFree(pointer); + GLib.GFree(pointer); - return managedArray; - } + return managedArray; } } \ No newline at end of file diff --git a/src/NetVips/Source.cs b/src/NetVips/Source.cs index d53d344c..68a7acb4 100644 --- a/src/NetVips/Source.cs +++ b/src/NetVips/Source.cs @@ -1,147 +1,146 @@ -namespace NetVips -{ - using System; - using System.Text; - using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace NetVips; +/// +/// An input connection. +/// +public class Source : Connection +{ /// - /// An input connection. + /// Secret ref for . /// - public class Source : Connection + private GCHandle _dataHandle; + + /// + internal Source(IntPtr pointer) + : base(pointer) { - /// - /// Secret ref for . - /// - private GCHandle _dataHandle; + } - /// - internal Source(IntPtr pointer) - : base(pointer) + /// + /// Make a new source from a file descriptor (a small integer). + /// + /// + /// Make a new source that is attached to the descriptor. For example: + /// + /// using var source = Source.NewFromDescriptor(0); + /// + /// Makes a descriptor attached to stdin. + /// + /// You can pass this source to (for example) . + /// + /// Read from this file descriptor. + /// A new . + /// If unable to create a new from . + public static Source NewFromDescriptor(int descriptor) + { + var pointer = Internal.VipsSource.NewFromDescriptor(descriptor); + if (pointer == IntPtr.Zero) { + throw new VipsException($"can't create source from descriptor {descriptor}"); } - /// - /// Make a new source from a file descriptor (a small integer). - /// - /// - /// Make a new source that is attached to the descriptor. For example: - /// - /// using var source = Source.NewFromDescriptor(0); - /// - /// Makes a descriptor attached to stdin. - /// - /// You can pass this source to (for example) . - /// - /// Read from this file descriptor. - /// A new . - /// If unable to create a new from . - public static Source NewFromDescriptor(int descriptor) - { - var pointer = Internal.VipsSource.NewFromDescriptor(descriptor); - if (pointer == IntPtr.Zero) - { - throw new VipsException($"can't create source from descriptor {descriptor}"); - } - - return new Source(pointer); - } + return new Source(pointer); + } - /// - /// Make a new source from a filename. - /// - /// - /// Make a new source that is attached to the named file. For example: - /// - /// using var source = Source.NewFromFile("myfile.jpg"); - /// - /// You can pass this source to (for example) . - /// - /// Read from this filename. - /// A new . - /// If unable to create a new from . - public static Source NewFromFile(string filename) + /// + /// Make a new source from a filename. + /// + /// + /// Make a new source that is attached to the named file. For example: + /// + /// using var source = Source.NewFromFile("myfile.jpg"); + /// + /// You can pass this source to (for example) . + /// + /// Read from this filename. + /// A new . + /// If unable to create a new from . + public static Source NewFromFile(string filename) + { + var bytes = Encoding.UTF8.GetBytes(filename + char.MinValue); // Ensure null-terminated string + var pointer = Internal.VipsSource.NewFromFile(bytes); + if (pointer == IntPtr.Zero) { - var bytes = Encoding.UTF8.GetBytes(filename + char.MinValue); // Ensure null-terminated string - var pointer = Internal.VipsSource.NewFromFile(bytes); - if (pointer == IntPtr.Zero) - { - throw new VipsException($"can't create source from filename {filename}"); - } - - return new Source(pointer); + throw new VipsException($"can't create source from filename {filename}"); } - /// - /// Make a new source from a memory object. - /// - /// - /// Make a new source that is attached to the memory object. For example: - /// - /// using var source = Source.NewFromMemory(data); - /// - /// You can pass this source to (for example) . - /// - /// The memory object. - /// A new . - /// If unable to create a new from . - public static Source NewFromMemory(byte[] data) + return new Source(pointer); + } + + /// + /// Make a new source from a memory object. + /// + /// + /// Make a new source that is attached to the memory object. For example: + /// + /// using var source = Source.NewFromMemory(data); + /// + /// You can pass this source to (for example) . + /// + /// The memory object. + /// A new . + /// If unable to create a new from . + public static Source NewFromMemory(byte[] data) + { + var handle = GCHandle.Alloc(data, GCHandleType.Pinned); + var pointer = Internal.VipsSource.NewFromMemory(handle.AddrOfPinnedObject(), (nuint)data.Length); + if (pointer == IntPtr.Zero) { - var handle = GCHandle.Alloc(data, GCHandleType.Pinned); - var pointer = Internal.VipsSource.NewFromMemory(handle.AddrOfPinnedObject(), (nuint)data.Length); - if (pointer == IntPtr.Zero) + if (handle.IsAllocated) { - if (handle.IsAllocated) - { - handle.Free(); - } - - throw new VipsException("can't create input source from memory"); + handle.Free(); } - return new Source(pointer) { _dataHandle = handle }; + throw new VipsException("can't create input source from memory"); } - /// - /// Make a new source from a memory object. - /// - /// - /// Make a new source that is attached to the memory object. For example: - /// - /// using var source = Source.NewFromMemory(data); - /// - /// You can pass this source to (for example) . - /// - /// The memory object. - /// A new . - /// If unable to create a new from . - public static Source NewFromMemory(string data) => NewFromMemory(Encoding.UTF8.GetBytes(data)); + return new Source(pointer) { _dataHandle = handle }; + } + + /// + /// Make a new source from a memory object. + /// + /// + /// Make a new source that is attached to the memory object. For example: + /// + /// using var source = Source.NewFromMemory(data); + /// + /// You can pass this source to (for example) . + /// + /// The memory object. + /// A new . + /// If unable to create a new from . + public static Source NewFromMemory(string data) => NewFromMemory(Encoding.UTF8.GetBytes(data)); - /// - /// Make a new source from a memory object. - /// - /// - /// Make a new source that is attached to the memory object. For example: - /// - /// using var source = Source.NewFromMemory(data); - /// - /// You can pass this source to (for example) . - /// - /// The memory object. - /// A new . - /// If unable to create a new from . - public static Source NewFromMemory(char[] data) => NewFromMemory(Encoding.UTF8.GetBytes(data)); + /// + /// Make a new source from a memory object. + /// + /// + /// Make a new source that is attached to the memory object. For example: + /// + /// using var source = Source.NewFromMemory(data); + /// + /// You can pass this source to (for example) . + /// + /// The memory object. + /// A new . + /// If unable to create a new from . + public static Source NewFromMemory(char[] data) => NewFromMemory(Encoding.UTF8.GetBytes(data)); - /// - protected override void Dispose(bool disposing) + /// + protected override void Dispose(bool disposing) + { + // release reference to our secret ref + if (_dataHandle.IsAllocated) { - // release reference to our secret ref - if (_dataHandle.IsAllocated) - { - _dataHandle.Free(); - } - - // Call our base Dispose method - base.Dispose(disposing); + _dataHandle.Free(); } + + // Call our base Dispose method + base.Dispose(disposing); } } \ No newline at end of file diff --git a/src/NetVips/SourceCustom.cs b/src/NetVips/SourceCustom.cs index b9f428c3..f372cf8d 100644 --- a/src/NetVips/SourceCustom.cs +++ b/src/NetVips/SourceCustom.cs @@ -1,118 +1,117 @@ -namespace NetVips +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 { - using System.IO; - using System.Buffers; - using System.Runtime.InteropServices; + /// + /// 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); /// - /// An source you can connect delegates to implement behaviour. + /// A seek delegate. /// - 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); + /// + /// 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); - /// - /// 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 read delegate. - /// - public event ReadDelegate OnRead; + /// + /// Attach a seek delegate. + /// + public event SeekDelegate OnSeek; - /// - /// 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); + } - /// - public SourceCustom() : base(Internal.VipsSourceCustom.New()) + /// + /// 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) { - SignalConnect("read", (Internal.VipsSourceCustom.ReadSignal)ReadHandler); - SignalConnect("seek", (Internal.VipsSourceCustom.SeekSignal)SeekHandler); + return 0; } - /// - /// 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) + var tempArray = ArrayPool.Shared.Rent((int)length); + try { - 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 + var readLength = OnRead?.Invoke(tempArray, (int)length); + if (!readLength.HasValue) { return -1; } - finally + + if (readLength.Value > 0) { - ArrayPool.Shared.Return(tempArray); + Marshal.Copy(tempArray, 0, buffer, readLength.Value); } - } - /// - /// 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) + return readLength.Value; + } + catch { - var newPosition = OnSeek?.Invoke(offset, (SeekOrigin)whence); - return newPosition ?? -1; + 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; } } \ No newline at end of file diff --git a/src/NetVips/SourceStream.cs b/src/NetVips/SourceStream.cs index d8663f8b..4fb191b0 100644 --- a/src/NetVips/SourceStream.cs +++ b/src/NetVips/SourceStream.cs @@ -1,89 +1,88 @@ -namespace NetVips +using System; +using System.IO; + +namespace NetVips; + +/// +/// An source connected to a readable . +/// +internal class SourceStream : SourceCustom { - using System; - using System.IO; + /// + /// Read from this stream. + /// + private readonly Stream _stream; /// - /// An source connected to a readable . + /// The start position within the stream. /// - internal class SourceStream : SourceCustom + private readonly long _startPosition; + + /// + internal SourceStream(Stream stream) { - /// - /// Read from this stream. - /// - private readonly Stream _stream; + var seekable = stream.CanSeek; - /// - /// The start position within the stream. - /// - private readonly long _startPosition; + _stream = stream; + _startPosition = seekable ? _stream.Position : 0; - /// - internal SourceStream(Stream stream) + OnRead += Read; + if (seekable) { - var seekable = stream.CanSeek; - - _stream = stream; - _startPosition = seekable ? _stream.Position : 0; - - OnRead += Read; - if (seekable) - { - OnSeek += Seek; - } + OnSeek += Seek; } + } - /// - /// Create a attached to an . - /// - /// Read from this stream. - /// A new . - /// If is not readable. - internal static SourceStream NewFromStream(Stream stream) + /// + /// Create a attached to an . + /// + /// Read from this stream. + /// A new . + /// If is not readable. + internal static SourceStream NewFromStream(Stream stream) + { + if (!stream.CanRead) { - if (!stream.CanRead) - { - throw new ArgumentException("The stream should be readable.", nameof(stream)); - } - - return new SourceStream(stream); + throw new ArgumentException("The stream should be readable.", nameof(stream)); } - /// - /// Attach a read handler. - /// - /// An array of bytes. - /// The maximum number of bytes to be read. - /// The total number of bytes read into the buffer. - public int Read(byte[] buffer, int length) - { - return _stream.Read(buffer, 0, length); - } + return new SourceStream(stream); + } + + /// + /// Attach a read handler. + /// + /// An array of bytes. + /// The maximum number of bytes to be read. + /// The total number of bytes read into the buffer. + public int Read(byte[] buffer, int length) + { + return _stream.Read(buffer, 0, length); + } - /// - /// Attach a seek handler. - /// - /// 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 stream. - public long Seek(long offset, SeekOrigin origin) + /// + /// Attach a seek handler. + /// + /// 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 stream. + public long Seek(long offset, SeekOrigin origin) + { + try { - try + return origin switch { - return origin switch - { - SeekOrigin.Begin => _stream.Seek(_startPosition + offset, SeekOrigin.Begin) - _startPosition, - SeekOrigin.Current => _stream.Seek(offset, SeekOrigin.Current) - _startPosition, - SeekOrigin.End => _stream.Seek(offset, SeekOrigin.End) - _startPosition, - _ => -1 - }; - } - catch - { - return -1; - } + SeekOrigin.Begin => _stream.Seek(_startPosition + offset, SeekOrigin.Begin) - _startPosition, + SeekOrigin.Current => _stream.Seek(offset, SeekOrigin.Current) - _startPosition, + SeekOrigin.End => _stream.Seek(offset, SeekOrigin.End) - _startPosition, + _ => -1 + }; + } + catch + { + return -1; } } } \ No newline at end of file diff --git a/src/NetVips/Stats.cs b/src/NetVips/Stats.cs index 8f4c3444..a78a8225 100644 --- a/src/NetVips/Stats.cs +++ b/src/NetVips/Stats.cs @@ -1,40 +1,39 @@ -namespace NetVips +using NetVips.Internal; + +namespace NetVips; + +/// +/// A class that provides the statistics of memory usage and opened files. +/// +/// +/// libvips watches the total amount of live tracked memory and +/// uses this information to decide when to trim caches. +/// +public static class Stats { - using Internal; + /// + /// Get the number of active allocations. + /// + public static int Allocations => Vips.TrackedGetAllocs(); /// - /// A class that provides the statistics of memory usage and opened files. + /// Get the number of bytes currently allocated `vips_malloc()` and friends. /// /// - /// libvips watches the total amount of live tracked memory and - /// uses this information to decide when to trim caches. + /// libvips uses this figure to decide when to start dropping cache. /// - public static class Stats - { - /// - /// Get the number of active allocations. - /// - public static int Allocations => Vips.TrackedGetAllocs(); - - /// - /// Get the number of bytes currently allocated `vips_malloc()` and friends. - /// - /// - /// libvips uses this figure to decide when to start dropping cache. - /// - public static int Mem => Vips.TrackedGetMem(); + public static int Mem => Vips.TrackedGetMem(); - /// - /// Returns the largest number of bytes simultaneously allocated via vips_tracked_malloc(). - /// - /// - /// Handy for estimating max memory requirements for a program. - /// - public static ulong MemHighwater => Vips.TrackedGetMemHighwater(); + /// + /// Returns the largest number of bytes simultaneously allocated via vips_tracked_malloc(). + /// + /// + /// Handy for estimating max memory requirements for a program. + /// + public static ulong MemHighwater => Vips.TrackedGetMemHighwater(); - /// - /// Get the number of open files. - /// - public static int Files => Vips.TrackedGetFiles(); - } + /// + /// Get the number of open files. + /// + public static int Files => Vips.TrackedGetFiles(); } \ No newline at end of file diff --git a/src/NetVips/Target.cs b/src/NetVips/Target.cs index 9fb3f80c..b296a471 100644 --- a/src/NetVips/Target.cs +++ b/src/NetVips/Target.cs @@ -1,100 +1,99 @@ -namespace NetVips +using System; +using System.Text; + +namespace NetVips; + +/// +/// An output connection. +/// +public class Target : Connection { - using System; - using System.Text; + /// + internal Target(IntPtr pointer) : base(pointer) + { + } /// - /// An output connection. + /// Get the memory object held by the target when using . /// - public class Target : Connection + public byte[] Blob => (byte[])Get("blob"); + + /// + /// Make a new target to write to a file descriptor (a small integer). + /// + /// + /// Make a new target that is attached to the descriptor. For example: + /// + /// using var target = Target.NewToDescriptor(1); + /// + /// Makes a descriptor attached to stdout. + /// + /// You can pass this target to (for example) . + /// + /// Write to this file descriptor. + /// A new . + /// If unable to create a new from . + public static Target NewToDescriptor(int descriptor) { - /// - internal Target(IntPtr pointer) : base(pointer) + var pointer = Internal.VipsTarget.NewToDescriptor(descriptor); + if (pointer == IntPtr.Zero) { + throw new VipsException($"can't create output target to descriptor {descriptor}"); } - /// - /// Get the memory object held by the target when using . - /// - public byte[] Blob => (byte[])Get("blob"); + return new Target(pointer); + } - /// - /// Make a new target to write to a file descriptor (a small integer). - /// - /// - /// Make a new target that is attached to the descriptor. For example: - /// - /// using var target = Target.NewToDescriptor(1); - /// - /// Makes a descriptor attached to stdout. - /// - /// You can pass this target to (for example) . - /// - /// Write to this file descriptor. - /// A new . - /// If unable to create a new from . - public static Target NewToDescriptor(int descriptor) + /// + /// Make a new target to write to a file. + /// + /// + /// Make a new target that will write to the named file. For example: + /// + /// using var target = Target.NewToFile("myfile.jpg"); + /// + /// You can pass this target to (for example) . + /// + /// Write to this this file. + /// A new . + /// If unable to create a new from . + public static Target NewToFile(string filename) + { + var bytes = Encoding.UTF8.GetBytes(filename + char.MinValue); // Ensure null-terminated string + var pointer = Internal.VipsTarget.NewToFile(bytes); + if (pointer == IntPtr.Zero) { - var pointer = Internal.VipsTarget.NewToDescriptor(descriptor); - if (pointer == IntPtr.Zero) - { - throw new VipsException($"can't create output target to descriptor {descriptor}"); - } - - return new Target(pointer); + throw new VipsException($"can't create output target to filename {filename}"); } - /// - /// Make a new target to write to a file. - /// - /// - /// Make a new target that will write to the named file. For example: - /// - /// using var target = Target.NewToFile("myfile.jpg"); - /// - /// You can pass this target to (for example) . - /// - /// Write to this this file. - /// A new . - /// If unable to create a new from . - public static Target NewToFile(string filename) - { - var bytes = Encoding.UTF8.GetBytes(filename + char.MinValue); // Ensure null-terminated string - var pointer = Internal.VipsTarget.NewToFile(bytes); - if (pointer == IntPtr.Zero) - { - throw new VipsException($"can't create output target to filename {filename}"); - } - - return new Target(pointer); - } + return new Target(pointer); + } - /// - /// Make a new target to write to an area of memory. - /// - /// - /// Make a new target that will write to memory. For example: - /// - /// using var target = Target.NewToMemory(); - /// - /// You can pass this target to (for example) . - /// - /// After writing to the target, fetch the bytes from the target object with: - /// - /// var bytes = target.Blob; - /// - /// - /// A new . - /// If unable to create a new . - public static Target NewToMemory() + /// + /// Make a new target to write to an area of memory. + /// + /// + /// Make a new target that will write to memory. For example: + /// + /// using var target = Target.NewToMemory(); + /// + /// You can pass this target to (for example) . + /// + /// After writing to the target, fetch the bytes from the target object with: + /// + /// var bytes = target.Blob; + /// + /// + /// A new . + /// If unable to create a new . + public static Target NewToMemory() + { + var pointer = Internal.VipsTarget.NewToMemory(); + if (pointer == IntPtr.Zero) { - var pointer = Internal.VipsTarget.NewToMemory(); - if (pointer == IntPtr.Zero) - { - throw new VipsException("can't create output target to memory"); - } - - return new Target(pointer); + throw new VipsException("can't create output target to memory"); } + + return new Target(pointer); } } \ No newline at end of file diff --git a/src/NetVips/TargetCustom.cs b/src/NetVips/TargetCustom.cs index 449bc8ee..9722c02e 100644 --- a/src/NetVips/TargetCustom.cs +++ b/src/NetVips/TargetCustom.cs @@ -1,181 +1,180 @@ -namespace NetVips +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 { - using System.IO; - using System.Buffers; - using System.Runtime.InteropServices; + /// + /// 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; /// - /// An target you can connect delegates to implement behaviour. + /// Attach a end delegate. /// - public class TargetCustom : Target + public event EndDelegate OnEnd; + + /// + public TargetCustom() : base(Internal.VipsTargetCustom.New()) { - /// - /// 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); + 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); + 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) + /// + /// 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) { - var bytesWritten = OnWrite?.Invoke(buffer, length); - return bytesWritten ?? -1; + return 0; } - /// - /// 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) + var tempArray = ArrayPool.Shared.Rent((int)length); + try { - 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 + var readLength = OnRead?.Invoke(tempArray, (int)length); + if (!readLength.HasValue) { return -1; } - finally + + if (readLength.Value > 0) { - ArrayPool.Shared.Return(tempArray); + Marshal.Copy(tempArray, 0, buffer, readLength.Value); } - } - /// - /// 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) + return readLength.Value; + } + catch { - var newPosition = OnSeek?.Invoke(offset, (SeekOrigin)whence); - return newPosition ?? -1; + return -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) + finally { - return OnEnd?.Invoke() ?? 0; + 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; + } } \ No newline at end of file diff --git a/src/NetVips/TargetStream.cs b/src/NetVips/TargetStream.cs index 23a04377..50afe8cc 100644 --- a/src/NetVips/TargetStream.cs +++ b/src/NetVips/TargetStream.cs @@ -1,133 +1,132 @@ -namespace NetVips +using System; +using System.IO; + +namespace NetVips; + +/// +/// An target connected to a writable . +/// +internal class TargetStream : TargetCustom { - using System; - using System.IO; + /// + /// Write to this stream. + /// + private readonly Stream _stream; /// - /// An target connected to a writable . + /// The start position within the stream. /// - internal class TargetStream : TargetCustom + private readonly long _startPosition; + + /// + internal TargetStream(Stream stream) { - /// - /// Write to this stream. - /// - private readonly Stream _stream; + var readable = stream.CanRead; + var seekable = stream.CanSeek; - /// - /// The start position within the stream. - /// - private readonly long _startPosition; + _stream = stream; + _startPosition = seekable ? _stream.Position : 0; - /// - internal TargetStream(Stream stream) + OnWrite += Write; + if (readable) { - var readable = stream.CanRead; - var seekable = stream.CanSeek; - - _stream = stream; - _startPosition = seekable ? _stream.Position : 0; - - OnWrite += Write; - if (readable) - { - OnRead += Read; - } - if (seekable) - { - OnSeek += Seek; - } - OnEnd += End; + OnRead += Read; } - - /// - /// Create a which will output to a . - /// - /// Write to this stream. - /// A new . - /// If is not writable. - internal static TargetStream NewFromStream(Stream stream) + if (seekable) { - if (!stream.CanWrite) - { - throw new ArgumentException("The stream should be writable.", nameof(stream)); - } - - return new TargetStream(stream); + OnSeek += Seek; } + OnEnd += End; + } - /// - /// Attach a write handler. - /// - /// An array of bytes. - /// The number of bytes to be written to the current stream. - /// The total number of bytes written to the stream. - private long Write(byte[] buffer, int length) + /// + /// Create a which will output to a . + /// + /// Write to this stream. + /// A new . + /// If is not writable. + internal static TargetStream NewFromStream(Stream stream) + { + if (!stream.CanWrite) { - try - { - _stream.Write(buffer, 0, length); - } - catch - { - return -1; - } - - return length; + throw new ArgumentException("The stream should be writable.", nameof(stream)); } - /// - /// Attach a read handler. - /// - /// An array of bytes. - /// The maximum number of bytes to be read. - /// The total number of bytes read into the buffer. - public int Read(byte[] buffer, int length) + return new TargetStream(stream); + } + + /// + /// Attach a write handler. + /// + /// An array of bytes. + /// The number of bytes to be written to the current stream. + /// The total number of bytes written to the stream. + private long Write(byte[] buffer, int length) + { + try { - return _stream.Read(buffer, 0, length); + _stream.Write(buffer, 0, length); } - - /// - /// Attach a seek handler. - /// - /// 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 stream. - public long Seek(long offset, SeekOrigin origin) + catch { - try - { - return origin switch - { - SeekOrigin.Begin => _stream.Seek(_startPosition + offset, SeekOrigin.Begin) - _startPosition, - SeekOrigin.Current => _stream.Seek(offset, SeekOrigin.Current) - _startPosition, - SeekOrigin.End => _stream.Seek(offset, SeekOrigin.End) - _startPosition, - _ => -1 - }; - } - catch - { - return -1; - } + return -1; } - /// - /// Attach a end handler. - /// - /// 0 on success, -1 on error. - public int End() + return length; + } + + /// + /// Attach a read handler. + /// + /// An array of bytes. + /// The maximum number of bytes to be read. + /// The total number of bytes read into the buffer. + public int Read(byte[] buffer, int length) + { + return _stream.Read(buffer, 0, length); + } + + /// + /// Attach a seek handler. + /// + /// 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 stream. + public long Seek(long offset, SeekOrigin origin) + { + try { - try + return origin switch { - _stream.Flush(); - } - catch - { - return -1; - } + SeekOrigin.Begin => _stream.Seek(_startPosition + offset, SeekOrigin.Begin) - _startPosition, + SeekOrigin.Current => _stream.Seek(offset, SeekOrigin.Current) - _startPosition, + SeekOrigin.End => _stream.Seek(offset, SeekOrigin.End) - _startPosition, + _ => -1 + }; + } + catch + { + return -1; + } + } - return 0; + /// + /// Attach a end handler. + /// + /// 0 on success, -1 on error. + public int End() + { + try + { + _stream.Flush(); } + catch + { + return -1; + } + + return 0; } } \ No newline at end of file diff --git a/src/NetVips/VOption.cs b/src/NetVips/VOption.cs index ffada4b3..a0f1e1c1 100644 --- a/src/NetVips/VOption.cs +++ b/src/NetVips/VOption.cs @@ -1,117 +1,116 @@ -namespace NetVips +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace NetVips; + +/// +/// This class wraps a . +/// This is used to call functions with optional arguments. See . +/// +public class VOption : IEnumerable> { - using System.Collections; - using System.Collections.Generic; - using System.Runtime.CompilerServices; + private readonly Dictionary _internalDictionary = new(); /// - /// This class wraps a . - /// This is used to call functions with optional arguments. See . + /// Returns an enumerator that iterates through the . /// - public class VOption : IEnumerable> - { - private readonly Dictionary _internalDictionary = new(); + /// A structure for the . + public IEnumerator> GetEnumerator() => _internalDictionary.GetEnumerator(); - /// - /// Returns an enumerator that iterates through the . - /// - /// A structure for the . - public IEnumerator> GetEnumerator() => _internalDictionary.GetEnumerator(); + /// + IEnumerator IEnumerable.GetEnumerator() => _internalDictionary.GetEnumerator(); - /// - IEnumerator IEnumerable.GetEnumerator() => _internalDictionary.GetEnumerator(); - - /// - /// Gets or sets the value associated with the specified key. - /// - /// The key of the value to get or set. - /// The value associated with the specified key. - public object this[string key] - { - get => _internalDictionary[key]; - set => Add(key, value); - } + /// + /// Gets or sets the value associated with the specified key. + /// + /// The key of the value to get or set. + /// The value associated with the specified key. + public object this[string key] + { + get => _internalDictionary[key]; + set => Add(key, value); + } - /// - /// Gets a collection containing the keys in the . - /// - public Dictionary.KeyCollection Keys => _internalDictionary.Keys; + /// + /// Gets a collection containing the keys in the . + /// + public Dictionary.KeyCollection Keys => _internalDictionary.Keys; - /// - /// Gets the number of key/value pairs contained in the . - /// - /// The number of key/value pairs contained in the . - public int Count => _internalDictionary.Count; + /// + /// Gets the number of key/value pairs contained in the . + /// + /// The number of key/value pairs contained in the . + public int Count => _internalDictionary.Count; - /// - /// Adds the specified key and value to the . - /// - /// The key of the element to add. - /// The value of the element to add. The value can be null for reference types. - public void Add(string key, object value) => _internalDictionary.Add(key, value); + /// + /// Adds the specified key and value to the . + /// + /// The key of the element to add. + /// The value of the element to add. The value can be null for reference types. + public void Add(string key, object value) => _internalDictionary.Add(key, value); - /// - /// Adds the specified key and value to the , if value is present. - /// - /// The key of the element to add. - /// The value of the element to add. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddIfPresent(string key, T? value) where T : struct + /// + /// Adds the specified key and value to the , if value is present. + /// + /// The key of the element to add. + /// The value of the element to add. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddIfPresent(string key, T? value) where T : struct + { + if (value.HasValue) { - if (value.HasValue) - { - _internalDictionary.Add(key, value); - } + _internalDictionary.Add(key, value); } + } - /// - /// Adds the specified key and class to the , if class is present. - /// - /// The key of the element to add. - /// The value of the element to add. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddIfPresent(string key, T cls) where T : class + /// + /// Adds the specified key and class to the , if class is present. + /// + /// The key of the element to add. + /// The value of the element to add. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddIfPresent(string key, T cls) where T : class + { + if (cls != null) { - if (cls != null) - { - _internalDictionary.Add(key, cls); - } + _internalDictionary.Add(key, cls); } + } - /// - /// Adds the specified key and array to the , if array is present. - /// - /// The key of the element to add. - /// The value of the element to add. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddIfPresent(string key, T[] array) where T : struct + /// + /// Adds the specified key and array to the , if array is present. + /// + /// The key of the element to add. + /// The value of the element to add. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddIfPresent(string key, T[] array) where T : struct + { + if (array != null && array.Length > 0) { - if (array != null && array.Length > 0) - { - _internalDictionary.Add(key, array); - } + _internalDictionary.Add(key, array); } + } - /// - /// Determines whether the contains the specified key. - /// - /// The key to locate in the . - /// if the contains an element with the specified key; otherwise, . - public bool ContainsKey(string key) => _internalDictionary.ContainsKey(key); + /// + /// Determines whether the contains the specified key. + /// + /// The key to locate in the . + /// if the contains an element with the specified key; otherwise, . + public bool ContainsKey(string key) => _internalDictionary.ContainsKey(key); - /// - /// Removes the value with the specified key from the . - /// - /// The key of the element to remove. - /// if the element is successfully found and removed; otherwise, . - public bool Remove(string key) => _internalDictionary.Remove(key); + /// + /// Removes the value with the specified key from the . + /// + /// The key of the element to remove. + /// if the element is successfully found and removed; otherwise, . + public bool Remove(string key) => _internalDictionary.Remove(key); - /// - /// Gets the value associated with the specified key. - /// - /// The key of the value to get. - /// When this method returns, contains the value associated with the specified key, if the key is found; otherwise, the default value for the type of the value parameter. This parameter is passed uninitialized. - /// if the contains an element with the specified key; otherwise, . - public bool TryGetValue(string key, out object value) => _internalDictionary.TryGetValue(key, out value); - } + /// + /// Gets the value associated with the specified key. + /// + /// The key of the value to get. + /// When this method returns, contains the value associated with the specified key, if the key is found; otherwise, the default value for the type of the value parameter. This parameter is passed uninitialized. + /// if the contains an element with the specified key; otherwise, . + public bool TryGetValue(string key, out object value) => _internalDictionary.TryGetValue(key, out value); } \ No newline at end of file diff --git a/src/NetVips/VipsBlob.cs b/src/NetVips/VipsBlob.cs index 135293b3..abbf3cc4 100644 --- a/src/NetVips/VipsBlob.cs +++ b/src/NetVips/VipsBlob.cs @@ -1,66 +1,65 @@ -namespace NetVips +using System; +using System.Runtime.InteropServices; + +namespace NetVips; + +/// +/// Manage a . +/// +internal class VipsBlob : SafeHandle { - using System; - using System.Runtime.InteropServices; + /// + /// Initializes a new instance of the class + /// with the specified pointer to wrap around. + /// + /// The pointer to wrap around. + internal VipsBlob(IntPtr pointer) + : base(IntPtr.Zero, true) + { + // record the pointer we were given to manage + SetHandle(pointer); + } /// - /// Manage a . + /// Get the data from a . /// - internal class VipsBlob : SafeHandle + /// Return number of bytes of data. + /// A containing the data. + internal nint GetData(out nuint length) { - /// - /// Initializes a new instance of the class - /// with the specified pointer to wrap around. - /// - /// The pointer to wrap around. - internal VipsBlob(IntPtr pointer) - : base(IntPtr.Zero, true) - { - // record the pointer we were given to manage - SetHandle(pointer); - } + return Internal.VipsBlob.Get(this, out length); + } - /// - /// Get the data from a . - /// - /// Return number of bytes of data. - /// A containing the data. - internal nint GetData(out nuint length) + /// + /// Decreases the reference count of the blob. + /// When its reference count drops to 0, the blob is finalized (i.e. its memory is freed). + /// + /// if the handle is released successfully; otherwise, + /// in the event of a catastrophic failure, . + protected override bool ReleaseHandle() + { + if (!IsInvalid) { - return Internal.VipsBlob.Get(this, out length); + // Free the VipsArea + Internal.VipsArea.Unref(handle); } - /// - /// Decreases the reference count of the blob. - /// When its reference count drops to 0, the blob is finalized (i.e. its memory is freed). - /// - /// if the handle is released successfully; otherwise, - /// in the event of a catastrophic failure, . - protected override bool ReleaseHandle() - { - if (!IsInvalid) - { - // Free the VipsArea - Internal.VipsArea.Unref(handle); - } - - return true; - } + return true; + } - /// - /// Gets a value indicating whether the handle is invalid. - /// - /// if the handle is not valid; otherwise, . - public override bool IsInvalid => handle == IntPtr.Zero; + /// + /// Gets a value indicating whether the handle is invalid. + /// + /// if the handle is not valid; otherwise, . + public override bool IsInvalid => handle == IntPtr.Zero; - /// - /// Get the number of bytes of data. - /// - internal ulong Length => Marshal.PtrToStructure(handle).Length; + /// + /// Get the number of bytes of data. + /// + internal ulong Length => Marshal.PtrToStructure(handle).Length; - /// - /// Get the reference count of the blob. Handy for debugging. - /// - internal int RefCount => Marshal.PtrToStructure(handle).Count; - } + /// + /// Get the reference count of the blob. Handy for debugging. + /// + internal int RefCount => Marshal.PtrToStructure(handle).Count; } \ No newline at end of file diff --git a/src/NetVips/VipsException.cs b/src/NetVips/VipsException.cs index 75d731d7..ca70c86b 100644 --- a/src/NetVips/VipsException.cs +++ b/src/NetVips/VipsException.cs @@ -1,45 +1,44 @@ -namespace NetVips -{ - using System; - using Internal; +using System; +using NetVips.Internal; + +namespace NetVips; +/// +/// Our own exception class which handles the libvips error buffer. +/// +public class VipsException : Exception +{ /// - /// Our own exception class which handles the libvips error buffer. + /// Initializes a new instance of the class. /// - public class VipsException : Exception + public VipsException() { - /// - /// Initializes a new instance of the class. - /// - public VipsException() - { - } + } - /// - /// Initializes a new instance of the class with a specified error message. - /// - /// The message that describes the error. - public VipsException(string message) - : base($"{message}{Environment.NewLine}{VipsErrorBuffer()}") - { - Vips.ErrorClear(); - } + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The message that describes the error. + public VipsException(string message) + : base($"{message}{Environment.NewLine}{VipsErrorBuffer()}") + { + Vips.ErrorClear(); + } - /// - /// Initializes a new instance of the class with a specified error message - /// and a reference to the inner exception that is the cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. - public VipsException(string message, Exception inner) - : base($"{message}{Environment.NewLine}{VipsErrorBuffer()}", inner) - { - Vips.ErrorClear(); - } + /// + /// Initializes a new instance of the class with a specified error message + /// and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. + public VipsException(string message, Exception inner) + : base($"{message}{Environment.NewLine}{VipsErrorBuffer()}", inner) + { + Vips.ErrorClear(); + } - private static string VipsErrorBuffer() - { - return Vips.ErrorBuffer().ToUtf8String(); - } + private static string VipsErrorBuffer() + { + return Vips.ErrorBuffer().ToUtf8String(); } } \ No newline at end of file diff --git a/src/NetVips/VipsObject.cs b/src/NetVips/VipsObject.cs index 94a71fff..14580dc5 100644 --- a/src/NetVips/VipsObject.cs +++ b/src/NetVips/VipsObject.cs @@ -1,158 +1,157 @@ -namespace NetVips +using System; +using System.Runtime.InteropServices; +using NetVips.Internal; + +namespace NetVips; + +/// +/// Manage a . +/// +public class VipsObject : GObject { - using System; - using System.Runtime.InteropServices; - using Internal; + /// + /// Attach a post-close delegate. This is called on finalization. + /// + /// + /// Useful for e.g. deleting the file associated with a temp image. + /// + public event Action OnPostClose + { + add => SignalConnect("postclose", value); + remove => SignalHandlersDisconnectByFunc(value); + } + + /// + internal VipsObject(IntPtr pointer) + : base(pointer) + { + } /// - /// Manage a . + /// Print a table of all active libvips objects. Handy for debugging. /// - public class VipsObject : GObject + internal static void PrintAll() { - /// - /// Attach a post-close delegate. This is called on finalization. - /// - /// - /// Useful for e.g. deleting the file associated with a temp image. - /// - public event Action OnPostClose - { - add => SignalConnect("postclose", value); - remove => SignalHandlersDisconnectByFunc(value); - } + GC.Collect(); + Internal.VipsObject.PrintAll(); + } - /// - internal VipsObject(IntPtr pointer) - : base(pointer) - { - } + /// + /// slow! eeeeew. + /// + /// Arg to fetch. + /// The pspec for this arg. + private GParamSpec.Struct? GetPspec(string name) + { + var argument = Internal.VipsObject.GetArgument(this, name, out var pspec, out _, out _); - /// - /// Print a table of all active libvips objects. Handy for debugging. - /// - internal static void PrintAll() - { - GC.Collect(); - Internal.VipsObject.PrintAll(); - } + return argument != 0 + ? default(GParamSpec.Struct?) + : Marshal.PtrToStructure(pspec); + } - /// - /// slow! eeeeew. - /// - /// Arg to fetch. - /// The pspec for this arg. - private GParamSpec.Struct? GetPspec(string name) + /// + /// Get a GObject property. + /// + /// + /// The value of the property is converted to a C# value. + /// + /// Arg to fetch. + /// The GObject property. + internal object Get(string name) + { + var pspec = GetPspec(name); + if (!pspec.HasValue) { - var argument = Internal.VipsObject.GetArgument(this, name, out var pspec, out _, out _); - - return argument != 0 - ? default(GParamSpec.Struct?) - : Marshal.PtrToStructure(pspec); + throw new VipsException("Property not found."); } - /// - /// Get a GObject property. - /// - /// - /// The value of the property is converted to a C# value. - /// - /// Arg to fetch. - /// The GObject property. - internal object Get(string name) - { - var pspec = GetPspec(name); - if (!pspec.HasValue) - { - throw new VipsException("Property not found."); - } + var gtype = pspec.Value.ValueType; + using var gv = new GValue(); + gv.SetType(gtype); - var gtype = pspec.Value.ValueType; - using var gv = new GValue(); - gv.SetType(gtype); + // this will add a reference for GObject properties, that ref will be + // unreferenced when the GValue is finalized + Internal.GObject.GetProperty(this, name, ref gv.Struct); - // this will add a reference for GObject properties, that ref will be - // unreferenced when the GValue is finalized - Internal.GObject.GetProperty(this, name, ref gv.Struct); + return gv.Get(); + } - return gv.Get(); - } + /// + /// Set a GObject property. The value is converted to the property type, if possible. + /// + /// The name of the property to set. + /// The value. + /// The GType of the property. + internal void Set(nint gtype, string name, object value) + { + using var gv = new GValue(); + gv.SetType(gtype); + gv.Set(value); - /// - /// Set a GObject property. The value is converted to the property type, if possible. - /// - /// The name of the property to set. - /// The value. - /// The GType of the property. - internal void Set(nint gtype, string name, object value) - { - using var gv = new GValue(); - gv.SetType(gtype); - gv.Set(value); + Internal.GObject.SetProperty(this, name, in gv.Struct); + } - Internal.GObject.SetProperty(this, name, in gv.Struct); - } + /// + /// Set a series of properties using a string. + /// + /// + /// For example: + /// "fred=12, tile" + /// "[fred=12]" + /// + /// Arguments as a string. + /// on success; otherwise, . + internal bool SetString(string stringOptions) + { + var result = Internal.VipsObject.SetFromString(this, stringOptions); + return result == 0; + } - /// - /// Set a series of properties using a string. - /// - /// - /// For example: - /// "fred=12, tile" - /// "[fred=12]" - /// - /// Arguments as a string. - /// on success; otherwise, . - internal bool SetString(string stringOptions) - { - var result = Internal.VipsObject.SetFromString(this, stringOptions); - return result == 0; - } + /// + /// Get the GType of a GObject property. + /// + /// The name of the GType to get the type of. + /// A new instance of initialized to the GType or + /// if the property does not exist. + public nint GetTypeOf(string name) + { + var pspec = GetPspec(name); - /// - /// Get the GType of a GObject property. - /// - /// The name of the GType to get the type of. - /// A new instance of initialized to the GType or - /// if the property does not exist. - public nint GetTypeOf(string name) + if (!pspec.HasValue) { - var pspec = GetPspec(name); - - if (!pspec.HasValue) - { - // need to clear any error, this is horrible - Vips.ErrorClear(); - return IntPtr.Zero; - } - - return pspec.Value.ValueType; + // need to clear any error, this is horrible + Vips.ErrorClear(); + return IntPtr.Zero; } - /// - /// Get the blurb for a GObject property. - /// - /// Arg to fetch. - /// The blurb. - public string GetBlurb(string name) - { - var pspec = GetPspec(name); - - if (!pspec.HasValue) - { - return null; - } + return pspec.Value.ValueType; + } - var pspecValue = pspec.Value; - return Marshal.PtrToStringAnsi(GParamSpec.GetBlurb(in pspecValue)); - } + /// + /// Get the blurb for a GObject property. + /// + /// Arg to fetch. + /// The blurb. + public string GetBlurb(string name) + { + var pspec = GetPspec(name); - /// - /// Get the description of a GObject. - /// - /// The description of a GObject. - public string GetDescription() + if (!pspec.HasValue) { - return Marshal.PtrToStringAnsi(Internal.VipsObject.GetDescription(this)); + return null; } + + var pspecValue = pspec.Value; + return Marshal.PtrToStringAnsi(GParamSpec.GetBlurb(in pspecValue)); + } + + /// + /// Get the description of a GObject. + /// + /// The description of a GObject. + public string GetDescription() + { + return Marshal.PtrToStringAnsi(Internal.VipsObject.GetDescription(this)); } } \ No newline at end of file diff --git a/src/NetVips/VipsProgress.cs b/src/NetVips/VipsProgress.cs index 5734b614..112af853 100644 --- a/src/NetVips/VipsProgress.cs +++ b/src/NetVips/VipsProgress.cs @@ -1,69 +1,68 @@ -namespace NetVips -{ - using System.Runtime.InteropServices; +using System.Runtime.InteropServices; + +namespace NetVips; +/// +/// Records a start time, and counts microseconds elapsed since that time. +/// +[StructLayout(LayoutKind.Sequential)] +public struct GTimer +{ /// - /// Records a start time, and counts microseconds elapsed since that time. + /// Monotonic start time, in microseconds. /// - [StructLayout(LayoutKind.Sequential)] - public struct GTimer - { - /// - /// Monotonic start time, in microseconds. - /// - public ulong Start; + public ulong Start; - /// - /// Monotonic end time, in microseconds. - /// - public ulong End; + /// + /// Monotonic end time, in microseconds. + /// + public ulong End; - /// - /// Is the timer currently active? - /// - public int Active; - } + /// + /// Is the timer currently active? + /// + public int Active; +} +/// +/// Struct we keep a record of execution time in. Passed to eval signal so +/// it can assess progress. +/// +[StructLayout(LayoutKind.Sequential)] +public struct VipsProgress +{ /// - /// Struct we keep a record of execution time in. Passed to eval signal so - /// it can assess progress. + /// Image we are part of. /// - [StructLayout(LayoutKind.Sequential)] - public struct VipsProgress - { - /// - /// Image we are part of. - /// - private nint Im; + private nint Im; - /// - /// Time we have been running. - /// - public int Run; + /// + /// Time we have been running. + /// + public int Run; - /// - /// Estimated seconds of computation left. - /// - public int Eta; + /// + /// Estimated seconds of computation left. + /// + public int Eta; - /// - /// Number of pels we expect to calculate. - /// - public long TPels; + /// + /// Number of pels we expect to calculate. + /// + public long TPels; - /// - /// Number of pels calculated so far. - /// - public long NPels; + /// + /// Number of pels calculated so far. + /// + public long NPels; - /// - /// Percent complete. - /// - public int Percent; + /// + /// Percent complete. + /// + public int Percent; - /// - /// Start time. - /// - public GTimer Start; - } + /// + /// Start time. + /// + public GTimer Start; } \ No newline at end of file diff --git a/tests/NetVips.Benchmarks/Benchmark.cs b/tests/NetVips.Benchmarks/Benchmark.cs index 47e3194f..025430ee 100644 --- a/tests/NetVips.Benchmarks/Benchmark.cs +++ b/tests/NetVips.Benchmarks/Benchmark.cs @@ -1,195 +1,194 @@ -namespace NetVips.Benchmarks -{ - using System; - using System.IO; - using BenchmarkDotNet.Attributes; +using System; +using System.IO; +using BenchmarkDotNet.Attributes; - using ImageMagick; +using ImageMagick; - using SixLabors.ImageSharp; - using SixLabors.ImageSharp.Processing; - using SixLabors.ImageSharp.Processing.Processors; - // using SixLabors.ImageSharp.Processing.Processors.Convolution; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +// using SixLabors.ImageSharp.Processing.Processors.Convolution; - using SkiaSharp; +using SkiaSharp; - // Alias to handle conflicting namespaces - using NetVipsImage = Image; - using ImageSharpImage = SixLabors.ImageSharp.Image; - using ImageSharpRectangle = SixLabors.ImageSharp.Rectangle; +// Alias to handle conflicting namespaces +using NetVipsImage = NetVips.Image; +using ImageSharpImage = SixLabors.ImageSharp.Image; +using ImageSharpRectangle = SixLabors.ImageSharp.Rectangle; #if Windows_NT - using System.Drawing; - using System.Drawing.Drawing2D; - using System.Drawing.Imaging; - using SystemDrawingImage = System.Drawing.Image; - using SystemDrawingRectangle = System.Drawing.Rectangle; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using SystemDrawingImage = System.Drawing.Image; +using SystemDrawingRectangle = System.Drawing.Rectangle; #endif - [Config(typeof(Config))] - public class Benchmark +namespace NetVips.Benchmarks; + +[Config(typeof(Config))] +public class Benchmark +{ + private const int Quality = 75; + + private readonly IImageProcessor _processor = new ImageSharp.ConvolutionProcessor(new float[,] + { + {-1, -1, -1}, + {-1, 16, -1}, + {-1, -1, -1} + }, false); + + [GlobalSetup] + public void GlobalSetup() { - private const int Quality = 75; + // Turn off OpenCL acceleration + OpenCL.IsEnabled = false; + + // Disable libvips cache to ensure tests are as fair as they can be + Cache.Max = 0; + } - private readonly IImageProcessor _processor = new ImageSharp.ConvolutionProcessor(new float[,] + [Benchmark(Description = "NetVips", Baseline = true)] + [Arguments("t.tif", "t2.tif")] + [Arguments("t.jpg", "t2.jpg")] + public void NetVips(string input, string output) + { + using var im = NetVipsImage.NewFromFile(input, access: Enums.Access.Sequential); + using var crop = im.Crop(100, 100, im.Width - 200, im.Height - 200); + using var reduce = crop.Reduce(1.0 / 0.9, 1.0 / 0.9, kernel: Enums.Kernel.Linear); + using var mask = NetVipsImage.NewFromArray(new[,] { {-1, -1, -1}, {-1, 16, -1}, {-1, -1, -1} - }, false); + }, 8); + using var convolve = reduce.Conv(mask, precision: Enums.Precision.Integer); - [GlobalSetup] - public void GlobalSetup() - { - // Turn off OpenCL acceleration - OpenCL.IsEnabled = false; - - // Disable libvips cache to ensure tests are as fair as they can be - Cache.Max = 0; - } - - [Benchmark(Description = "NetVips", Baseline = true)] - [Arguments("t.tif", "t2.tif")] - [Arguments("t.jpg", "t2.jpg")] - public void NetVips(string input, string output) - { - using var im = NetVipsImage.NewFromFile(input, access: Enums.Access.Sequential); - using var crop = im.Crop(100, 100, im.Width - 200, im.Height - 200); - using var reduce = crop.Reduce(1.0 / 0.9, 1.0 / 0.9, kernel: Enums.Kernel.Linear); - using var mask = NetVipsImage.NewFromArray(new[,] - { - {-1, -1, -1}, - {-1, 16, -1}, - {-1, -1, -1} - }, 8); - using var convolve = reduce.Conv(mask, precision: Enums.Precision.Integer); - - // Default quality is 75 - convolve.WriteToFile(output); - } + // Default quality is 75 + convolve.WriteToFile(output); + } - [Benchmark(Description = "Magick.NET")] - [Arguments("t.tif", "t2.tif")] - [Arguments("t.jpg", "t2.jpg")] - public void MagickNet(string input, string output) - { - using var im = new MagickImage(input); - im.Interpolate = PixelInterpolateMethod.Bilinear; - im.FilterType = FilterType.Triangle; + [Benchmark(Description = "Magick.NET")] + [Arguments("t.tif", "t2.tif")] + [Arguments("t.jpg", "t2.jpg")] + public void MagickNet(string input, string output) + { + using var im = new MagickImage(input); + im.Interpolate = PixelInterpolateMethod.Bilinear; + im.FilterType = FilterType.Triangle; - im.Shave(100, 100); - im.Resize(new Percentage(90.0)); + im.Shave(100, 100); + im.Resize(new Percentage(90.0)); - var kernel = new ConvolveMatrix(3, -1, -1, -1, -1, 16, -1, -1, -1, -1); + var kernel = new ConvolveMatrix(3, -1, -1, -1, -1, 16, -1, -1, -1, -1); - im.SetArtifact("convolve:scale", "0.125"); - im.Convolve(kernel); + im.SetArtifact("convolve:scale", "0.125"); + im.Convolve(kernel); - // Default quality of ImageMagick is 92, we need 75 - im.Quality = Quality; + // Default quality of ImageMagick is 92, we need 75 + im.Quality = Quality; - im.Write(output); - } + im.Write(output); + } - [Benchmark(Description = "ImageSharp")] - [Arguments("t.tif", "t2.tif")] - [Arguments("t.jpg", "t2.jpg")] - public void ImageSharp(string input, string output) - { - using var image = ImageSharpImage.Load(input); - image.Mutate(x => x - .Crop(new ImageSharpRectangle(100, 100, image.Width - 200, image.Height - 200)) - .Resize((int)Math.Round(image.Width * .9F), (int)Math.Round(image.Height * .9F), - KnownResamplers.Triangle) - .ApplyProcessor(_processor, image.Bounds)); - - // Default quality is 75 - image.Save(output); - } + [Benchmark(Description = "ImageSharp")] + [Arguments("t.tif", "t2.tif")] + [Arguments("t.jpg", "t2.jpg")] + public void ImageSharp(string input, string output) + { + using var image = ImageSharpImage.Load(input); + image.Mutate(x => x + .Crop(new ImageSharpRectangle(100, 100, image.Width - 200, image.Height - 200)) + .Resize((int)Math.Round(image.Width * .9F), (int)Math.Round(image.Height * .9F), + KnownResamplers.Triangle) + .ApplyProcessor(_processor, image.Bounds)); + + // Default quality is 75 + image.Save(output); + } - [Benchmark(Description = "SkiaSharp1")] - [Arguments("t.jpg", "t2.jpg")] // SkiaSharp doesn't have TIFF support - public void SkiaSharp(string input, string output) + [Benchmark(Description = "SkiaSharp1")] + [Arguments("t.jpg", "t2.jpg")] // SkiaSharp doesn't have TIFF support + public void SkiaSharp(string input, string output) + { + using var bitmap = SKBitmap.Decode(input); + bitmap.ExtractSubset(bitmap, SKRectI.Create(100, 100, bitmap.Width - 200, bitmap.Height - 200)); + + var targetWidth = (int)Math.Round(bitmap.Width * .9F); + var targetHeight = (int)Math.Round(bitmap.Height * .9F); + + // bitmap.Resize(new SKImageInfo(targetWidth, targetHeight), SKBitmapResizeMethod.Triangle) + // is deprecated, so we use `SKFilterQuality.Low` instead, see: + // https://github.com/mono/SkiaSharp/blob/1527bf392ebc7b4b57c992ef8bfe14c9899f76a3/binding/Binding/SKBitmap.cs#L24 + using var resized = bitmap.Resize(new SKImageInfo(targetWidth, targetHeight), SKFilterQuality.Low); + using var surface = + SKSurface.Create(new SKImageInfo(targetWidth, targetHeight, bitmap.ColorType, bitmap.AlphaType)); + using var canvas = surface.Canvas; + using var paint = new SKPaint { FilterQuality = SKFilterQuality.High }; + var kernel = new[] { - using var bitmap = SKBitmap.Decode(input); - bitmap.ExtractSubset(bitmap, SKRectI.Create(100, 100, bitmap.Width - 200, bitmap.Height - 200)); - - var targetWidth = (int)Math.Round(bitmap.Width * .9F); - var targetHeight = (int)Math.Round(bitmap.Height * .9F); - - // bitmap.Resize(new SKImageInfo(targetWidth, targetHeight), SKBitmapResizeMethod.Triangle) - // is deprecated, so we use `SKFilterQuality.Low` instead, see: - // https://github.com/mono/SkiaSharp/blob/1527bf392ebc7b4b57c992ef8bfe14c9899f76a3/binding/Binding/SKBitmap.cs#L24 - using var resized = bitmap.Resize(new SKImageInfo(targetWidth, targetHeight), SKFilterQuality.Low); - using var surface = - SKSurface.Create(new SKImageInfo(targetWidth, targetHeight, bitmap.ColorType, bitmap.AlphaType)); - using var canvas = surface.Canvas; - using var paint = new SKPaint { FilterQuality = SKFilterQuality.High }; - var kernel = new[] - { - -1f, -1f, -1f, - -1f, 16f, -1f, - -1f, -1f, -1f - }; - var kernelSize = new SKSizeI(3, 3); - var kernelOffset = new SKPointI(1, 1); - - paint.ImageFilter = SKImageFilter.CreateMatrixConvolution(kernelSize, kernel, 0.125f, 0f, kernelOffset, - SKShaderTileMode.Repeat, false); - - canvas.DrawBitmap(resized, 0, 0, paint); - canvas.Flush(); - - using var fileStream = File.OpenWrite(output); - surface.Snapshot() - .Encode(SKEncodedImageFormat.Jpeg, Quality) - .SaveTo(fileStream); - } + -1f, -1f, -1f, + -1f, 16f, -1f, + -1f, -1f, -1f + }; + var kernelSize = new SKSizeI(3, 3); + var kernelOffset = new SKPointI(1, 1); + + paint.ImageFilter = SKImageFilter.CreateMatrixConvolution(kernelSize, kernel, 0.125f, 0f, kernelOffset, + SKShaderTileMode.Repeat, false); + + canvas.DrawBitmap(resized, 0, 0, paint); + canvas.Flush(); + + using var fileStream = File.OpenWrite(output); + surface.Snapshot() + .Encode(SKEncodedImageFormat.Jpeg, Quality) + .SaveTo(fileStream); + } #if Windows_NT - [Benchmark(Description = "System.Drawing2")] - [Arguments("t.tif", "t2.tif")] - [Arguments("t.jpg", "t2.jpg")] - [System.Runtime.Versioning.SupportedOSPlatform("windows")] - public void SystemDrawing(string input, string output) + [Benchmark(Description = "System.Drawing2")] + [Arguments("t.tif", "t2.tif")] + [Arguments("t.jpg", "t2.jpg")] + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public void SystemDrawing(string input, string output) + { + using var image = SystemDrawingImage.FromFile(input, true); + var cropRect = new SystemDrawingRectangle(100, 100, image.Width - 200, image.Height - 200); + var resizeRect = new SystemDrawingRectangle(0, 0, (int)Math.Round(cropRect.Width * .9F), + (int)Math.Round(cropRect.Height * .9F)); + + using var src = new Bitmap(cropRect.Width, cropRect.Height); + using (var cropGraphics = Graphics.FromImage(src)) { - using var image = SystemDrawingImage.FromFile(input, true); - var cropRect = new SystemDrawingRectangle(100, 100, image.Width - 200, image.Height - 200); - var resizeRect = new SystemDrawingRectangle(0, 0, (int)Math.Round(cropRect.Width * .9F), - (int)Math.Round(cropRect.Height * .9F)); - - using var src = new Bitmap(cropRect.Width, cropRect.Height); - using (var cropGraphics = Graphics.FromImage(src)) - { - cropGraphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - cropGraphics.CompositingMode = CompositingMode.SourceCopy; - cropGraphics.InterpolationMode = InterpolationMode.Bilinear; - - // Crop - cropGraphics.DrawImage(image, new SystemDrawingRectangle(0, 0, src.Width, src.Height), cropRect, - GraphicsUnit.Pixel); - } - - using var resized = new Bitmap(resizeRect.Width, resizeRect.Height); - using var resizeGraphics = Graphics.FromImage(resized); - using var attributes = new ImageAttributes(); - - // Get rid of the annoying artifacts - attributes.SetWrapMode(WrapMode.TileFlipXY); - - resizeGraphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - resizeGraphics.CompositingMode = CompositingMode.SourceCopy; - resizeGraphics.InterpolationMode = InterpolationMode.Bilinear; - - // Resize - resizeGraphics.DrawImage(src, resizeRect, 0, 0, src.Width, src.Height, GraphicsUnit.Pixel, attributes); - - // No sharpening or convolution operation seems to be available - - // Default quality is 75, see: - // https://stackoverflow.com/a/3959115 - resized.Save(output); + cropGraphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + cropGraphics.CompositingMode = CompositingMode.SourceCopy; + cropGraphics.InterpolationMode = InterpolationMode.Bilinear; + + // Crop + cropGraphics.DrawImage(image, new SystemDrawingRectangle(0, 0, src.Width, src.Height), cropRect, + GraphicsUnit.Pixel); } -#endif + + using var resized = new Bitmap(resizeRect.Width, resizeRect.Height); + using var resizeGraphics = Graphics.FromImage(resized); + using var attributes = new ImageAttributes(); + + // Get rid of the annoying artifacts + attributes.SetWrapMode(WrapMode.TileFlipXY); + + resizeGraphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + resizeGraphics.CompositingMode = CompositingMode.SourceCopy; + resizeGraphics.InterpolationMode = InterpolationMode.Bilinear; + + // Resize + resizeGraphics.DrawImage(src, resizeRect, 0, 0, src.Width, src.Height, GraphicsUnit.Pixel, attributes); + + // No sharpening or convolution operation seems to be available + + // Default quality is 75, see: + // https://stackoverflow.com/a/3959115 + resized.Save(output); } +#endif } \ No newline at end of file diff --git a/tests/NetVips.Benchmarks/Config.cs b/tests/NetVips.Benchmarks/Config.cs index e7a3e9c1..e58c06d6 100644 --- a/tests/NetVips.Benchmarks/Config.cs +++ b/tests/NetVips.Benchmarks/Config.cs @@ -1,44 +1,44 @@ -namespace NetVips.Benchmarks -{ - using BenchmarkDotNet.Configs; - using BenchmarkDotNet.Environments; - using BenchmarkDotNet.Exporters; - using BenchmarkDotNet.Jobs; - using BenchmarkDotNet.Toolchains.CsProj; - using System.Reflection; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Toolchains.CsProj; +using System.Reflection; + +namespace NetVips.Benchmarks; - public class Config : ManualConfig +public class Config : ManualConfig +{ + public Config() { - public Config() - { - // Only support LTS and latest releases - // https://endoflife.date/dotnet - AddJob(Job.Default + // Only support LTS and latest releases + // https://endoflife.date/dotnet + AddJob(Job.Default #if NET6_0 - .WithToolchain(CsProjCoreToolchain.NetCoreApp60) - .WithId(".NET 6.0 CLI") + .WithToolchain(CsProjCoreToolchain.NetCoreApp60) + .WithId(".NET 6.0 CLI") #elif NET7_0 - .WithToolchain(CsProjCoreToolchain.NetCoreApp70) - .WithRuntime(NativeAotRuntime.Net70) - .WithId(".NET 7.0 CLI (NativeAOT)") -#elif NET8_0 - .WithToolchain(CsProjCoreToolchain.NetCoreApp80) - .WithRuntime(NativeAotRuntime.Net80) - .WithId(".NET 8.0 CLI (NativeAOT)") + .WithToolchain(CsProjCoreToolchain.NetCoreApp70) + .WithRuntime(NativeAotRuntime.Net70) + .WithId(".NET 7.0 CLI (NativeAOT)") +#elif NET8_0 + .WithToolchain(CsProjCoreToolchain.NetCoreApp80) + .WithRuntime(NativeAotRuntime.Net80) + .WithId(".NET 8.0 CLI (NativeAOT)") #endif #if GLOBAL_VIPS - .WithArguments(new Argument[] - { - new MsBuildArgument("/p:UseGlobalLibvips=true") - }) + .WithArguments(new Argument[] + { + new MsBuildArgument("/p:UseGlobalLibvips=true") + }) #endif - ); + ); - // Don't escape HTML within the GitHub Markdown exporter, - // to support
-tags within the "Method" column.
-            // Ouch, this is quite hackish.
-            var githubExporter = MarkdownExporter.GitHub;
-            githubExporter.GetType().GetField("EscapeHtml", BindingFlags.Instance | BindingFlags.NonPublic)!.SetValue(githubExporter, false);
-        }
+        // Don't escape HTML within the GitHub Markdown exporter,
+        // to support 
-tags within the "Method" column.
+        // Ouch, this is quite hackish.
+        var githubExporter = MarkdownExporter.GitHub;
+        githubExporter.GetType().GetField("EscapeHtml", BindingFlags.Instance | BindingFlags.NonPublic)!.SetValue(
+            githubExporter, false);
     }
 }
\ No newline at end of file
diff --git a/tests/NetVips.Benchmarks/ImageSharp/ConvolutionProcessor.cs b/tests/NetVips.Benchmarks/ImageSharp/ConvolutionProcessor.cs
index 38528677..c324da25 100644
--- a/tests/NetVips.Benchmarks/ImageSharp/ConvolutionProcessor.cs
+++ b/tests/NetVips.Benchmarks/ImageSharp/ConvolutionProcessor.cs
@@ -1,57 +1,56 @@
-namespace NetVips.Benchmarks.ImageSharp
-{
-    using System;
-    using System.Reflection;
-    using SixLabors.ImageSharp;
-    using SixLabors.ImageSharp.PixelFormats;
-    using SixLabors.ImageSharp.Processing.Processors;
+using System;
+using System.Reflection;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing.Processors;
+
+namespace NetVips.Benchmarks.ImageSharp;
 
+/// 
+/// Defines a processor that uses a 2 dimensional matrix to perform convolution against an image.
+/// 
+public sealed class ConvolutionProcessor : IImageProcessor
+{
     /// 
-    /// Defines a processor that uses a 2 dimensional matrix to perform convolution against an image.
+    /// Initializes a new instance of the  class.
     /// 
-    public sealed class ConvolutionProcessor : IImageProcessor
+    /// The 2d gradient operator.
+    /// Whether the convolution filter is applied to alpha as well as the color channels.
+    public ConvolutionProcessor(in DenseMatrix kernelXY, bool preserveAlpha)
     {
-        /// 
-        /// Initializes a new instance of the  class.
-        /// 
-        /// The 2d gradient operator.
-        /// Whether the convolution filter is applied to alpha as well as the color channels.
-        public ConvolutionProcessor(in DenseMatrix kernelXY, bool preserveAlpha)
-        {
-            KernelXY = kernelXY;
-            PreserveAlpha = preserveAlpha;
-        }
+        KernelXY = kernelXY;
+        PreserveAlpha = preserveAlpha;
+    }
 
-        /// 
-        /// Gets the 2d gradient operator.
-        /// 
-        public DenseMatrix KernelXY { get; }
+    /// 
+    /// Gets the 2d gradient operator.
+    /// 
+    public DenseMatrix KernelXY { get; }
 
-        /// 
-        /// Gets a value indicating whether the convolution filter is applied to alpha as well as the color channels.
-        /// 
-        public bool PreserveAlpha { get; }
+    /// 
+    /// Gets a value indicating whether the convolution filter is applied to alpha as well as the color channels.
+    /// 
+    public bool PreserveAlpha { get; }
 
-        /// 
-        public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration,
-            Image source,
-            Rectangle sourceRectangle) where TPixel : unmanaged, IPixel
+    /// 
+    public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration,
+        Image source,
+        Rectangle sourceRectangle) where TPixel : unmanaged, IPixel
+    {
+        var type = Type.GetType(
+            "SixLabors.ImageSharp.Processing.Processors.Convolution.ConvolutionProcessor`1, SixLabors.ImageSharp");
+        Type[] typeArgs = { typeof(TPixel) };
+        var genericType = type.MakeGenericType(typeArgs);
+        Type[] parameterTypes =
         {
-            var type = Type.GetType(
-                "SixLabors.ImageSharp.Processing.Processors.Convolution.ConvolutionProcessor`1, SixLabors.ImageSharp");
-            Type[] typeArgs = { typeof(TPixel) };
-            var genericType = type.MakeGenericType(typeArgs);
-            Type[] parameterTypes =
-            {
-                configuration.GetType(), KernelXY.GetType().MakeByRefType(), PreserveAlpha.GetType(), source.GetType(),
-                sourceRectangle.GetType()
-            };
-            var ctor = genericType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null,
-                parameterTypes, null);
-            var instance =
-                ctor.Invoke(new object[] { configuration, KernelXY, PreserveAlpha, source, sourceRectangle });
+            configuration.GetType(), KernelXY.GetType().MakeByRefType(), PreserveAlpha.GetType(), source.GetType(),
+            sourceRectangle.GetType()
+        };
+        var ctor = genericType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null,
+            parameterTypes, null);
+        var instance =
+            ctor.Invoke(new object[] { configuration, KernelXY, PreserveAlpha, source, sourceRectangle });
 
-            return (IImageProcessor)instance;
-        }
+        return (IImageProcessor)instance;
     }
 }
\ No newline at end of file
diff --git a/tests/NetVips.Benchmarks/Runner.cs b/tests/NetVips.Benchmarks/Runner.cs
index 8bce8b63..15ce8e40 100644
--- a/tests/NetVips.Benchmarks/Runner.cs
+++ b/tests/NetVips.Benchmarks/Runner.cs
@@ -1,14 +1,13 @@
-namespace NetVips.Benchmarks
-{
-    using System;
-    using BenchmarkDotNet.Running;
+using System;
+using BenchmarkDotNet.Running;
+
+namespace NetVips.Benchmarks;
 
-    public class Runner
+public class Runner
+{
+    public static void Main(string[] args)
     {
-        public static void Main(string[] args)
-        {
-            TestImage.BuildTestImages(AppDomain.CurrentDomain.BaseDirectory);
-            BenchmarkRunner.Run();
-        }
+        TestImage.BuildTestImages(AppDomain.CurrentDomain.BaseDirectory);
+        BenchmarkRunner.Run();
     }
 }
\ No newline at end of file
diff --git a/tests/NetVips.Benchmarks/TestImage.cs b/tests/NetVips.Benchmarks/TestImage.cs
index ed01b445..73fbfd9a 100644
--- a/tests/NetVips.Benchmarks/TestImage.cs
+++ b/tests/NetVips.Benchmarks/TestImage.cs
@@ -1,39 +1,38 @@
-namespace NetVips.Benchmarks
+using System;
+using System.IO;
+
+namespace NetVips.Benchmarks;
+
+public class TestImage
 {
-    using System;
-    using System.IO;
+    // Much larger than this and im falls over with cache resources exhausted
+    public const int TargetDimension = 5000;
 
-    public class TestImage
+    public static void BuildTestImages(string outputDir)
     {
-        // Much larger than this and im falls over with cache resources exhausted
-        public const int TargetDimension = 5000;
+        var targetTiff = Path.Combine(outputDir, "t.tif");
+        var targetJpeg = Path.Combine(outputDir, "t.jpg");
 
-        public static void BuildTestImages(string outputDir)
+        // Do not build test images if they are already present
+        if (File.Exists(targetTiff) && File.Exists(targetJpeg))
         {
-            var targetTiff = Path.Combine(outputDir, "t.tif");
-            var targetJpeg = Path.Combine(outputDir, "t.jpg");
-
-            // Do not build test images if they are already present
-            if (File.Exists(targetTiff) && File.Exists(targetJpeg))
-            {
-                return;
-            }
-
-            var outputFile = Path.Combine(outputDir, "t.v");
-
-            // Build test image
-            var im = Image.NewFromFile(Path.Combine(outputDir, "sample2.v"));
-            im = im.Replicate((int)Math.Ceiling((double)TargetDimension / im.Width),
-                (int)Math.Ceiling((double)TargetDimension / im.Height));
-            im = im.ExtractArea(0, 0, TargetDimension, TargetDimension);
-            im.WriteToFile(outputFile);
-
-            // Make tiff and jpeg derivatives
-            im = Image.NewFromFile(outputFile);
-            im.Tiffsave(targetTiff, tile: true);
-
-            im = Image.NewFromFile(outputFile);
-            im.Jpegsave(targetJpeg);
+            return;
         }
+
+        var outputFile = Path.Combine(outputDir, "t.v");
+
+        // Build test image
+        var im = Image.NewFromFile(Path.Combine(outputDir, "sample2.v"));
+        im = im.Replicate((int)Math.Ceiling((double)TargetDimension / im.Width),
+            (int)Math.Ceiling((double)TargetDimension / im.Height));
+        im = im.ExtractArea(0, 0, TargetDimension, TargetDimension);
+        im.WriteToFile(outputFile);
+
+        // Make tiff and jpeg derivatives
+        im = Image.NewFromFile(outputFile);
+        im.Tiffsave(targetTiff, tile: true);
+
+        im = Image.NewFromFile(outputFile);
+        im.Jpegsave(targetJpeg);
     }
 }
\ No newline at end of file
diff --git a/tests/NetVips.Tests/ArithmeticTests.cs b/tests/NetVips.Tests/ArithmeticTests.cs
index b63e7a23..dd548932 100644
--- a/tests/NetVips.Tests/ArithmeticTests.cs
+++ b/tests/NetVips.Tests/ArithmeticTests.cs
@@ -1,1280 +1,1279 @@
-namespace NetVips.Tests
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace NetVips.Tests;
+
+public class ArithmeticTests : IClassFixture
 {
-    using System;
-    using System.Collections.Generic;
-    using System.Linq;
-    using Xunit;
-    using Xunit.Abstractions;
+    private Image _image;
+    private Image _colour;
+    private Image _mono;
+    private Image[] _allImages;
 
-    public class ArithmeticTests : IClassFixture
+    public ArithmeticTests(TestsFixture testsFixture, ITestOutputHelper output)
     {
-        private Image _image;
-        private Image _colour;
-        private Image _mono;
-        private Image[] _allImages;
+        testsFixture.SetUpLogging(output);
 
-        public ArithmeticTests(TestsFixture testsFixture, ITestOutputHelper output)
+        _image = Image.MaskIdeal(100, 100, 0.5, reject: true, optical: true);
+        _colour = _image * new[] { 1, 2, 3 } + new[] { 2, 3, 4 };
+        _mono = _colour[1];
+        _allImages = new[]
         {
-            testsFixture.SetUpLogging(output);
+            _mono,
+            _colour
+        };
+    }
 
-            _image = Image.MaskIdeal(100, 100, 0.5, reject: true, optical: true);
-            _colour = _image * new[] { 1, 2, 3 } + new[] { 2, 3, 4 };
-            _mono = _colour[1];
-            _allImages = new[]
-            {
-                _mono,
-                _colour
-            };
-        }
+    #region helpers
 
-        #region helpers
+    internal void RunArith(Func func, Enums.BandFormat[] formats = null)
+    {
+        formats ??= Helper.AllFormats;
 
-        internal void RunArith(Func func, Enums.BandFormat[] formats = null)
+        foreach (var x in _allImages)
         {
-            formats ??= Helper.AllFormats;
-
-            foreach (var x in _allImages)
+            foreach (var y in formats)
             {
-                foreach (var y in formats)
+                foreach (var z in formats)
                 {
-                    foreach (var z in formats)
-                    {
-                        Helper.RunImage2(x.Cast(y), x.Cast(z), func);
-                    }
+                    Helper.RunImage2(x.Cast(y), x.Cast(z), func);
                 }
             }
         }
+    }
 
-        internal void RunArithConst(Func func, Enums.BandFormat[] formats = null)
-        {
-            formats ??= Helper.AllFormats;
-
-            foreach (var x in _allImages)
-            {
-                foreach (var y in formats)
-                {
-                    Helper.RunConst(func, x.Cast(y), (double)2);
-                }
-            }
+    internal void RunArithConst(Func func, Enums.BandFormat[] formats = null)
+    {
+        formats ??= Helper.AllFormats;
 
+        foreach (var x in _allImages)
+        {
             foreach (var y in formats)
             {
-                Helper.RunConst(func, _colour.Cast(y), new[] { 1, 2, 3 });
+                Helper.RunConst(func, x.Cast(y), (double)2);
             }
         }
 
-        /// 
-        /// run a function on an image,
-        /// 50,50 and 10,10 should have different values on the test image
-        /// 
-        /// 
-        /// 
-        /// None
-        internal void RunImageunary(Image im, Func func)
+        foreach (var y in formats)
         {
-            Helper.RunCmp(im, 50, 50, x => Helper.RunFn(func, x));
-            Helper.RunCmp(im, 10, 10, x => Helper.RunFn(func, x));
+            Helper.RunConst(func, _colour.Cast(y), new[] { 1, 2, 3 });
         }
+    }
 
-        internal void RunUnary(IEnumerable images, Func func, Enums.BandFormat[] formats = null)
-        {
-            formats ??= Helper.AllFormats;
+    /// 
+    /// run a function on an image,
+    /// 50,50 and 10,10 should have different values on the test image
+    /// 
+    /// 
+    /// 
+    /// None
+    internal void RunImageunary(Image im, Func func)
+    {
+        Helper.RunCmp(im, 50, 50, x => Helper.RunFn(func, x));
+        Helper.RunCmp(im, 10, 10, x => Helper.RunFn(func, x));
+    }
+
+    internal void RunUnary(IEnumerable images, Func func, Enums.BandFormat[] formats = null)
+    {
+        formats ??= Helper.AllFormats;
 
-            foreach (var x in images)
+        foreach (var x in images)
+        {
+            foreach (var y in formats)
             {
-                foreach (var y in formats)
-                {
-                    RunImageunary(x.Cast(y), func);
-                }
+                RunImageunary(x.Cast(y), func);
             }
         }
+    }
 
-        /// 
-        /// run a function on a pair of images
-        /// 50,50 and 10,10 should have different values on the test image
-        /// don't loop over band elements
-        /// 
-        /// 
-        /// 
-        /// 
-        internal void RunImageBinary(Image left, Image right, Func func)
-        {
-            Helper.RunCmp2(left, right, 50, 50, func);
-            Helper.RunCmp2(left, right, 10, 10, func);
-        }
+    /// 
+    /// run a function on a pair of images
+    /// 50,50 and 10,10 should have different values on the test image
+    /// don't loop over band elements
+    /// 
+    /// 
+    /// 
+    /// 
+    internal void RunImageBinary(Image left, Image right, Func func)
+    {
+        Helper.RunCmp2(left, right, 50, 50, func);
+        Helper.RunCmp2(left, right, 10, 10, func);
+    }
 
-        internal void RunBinary(IEnumerable images, Func func,
-            Enums.BandFormat[] formats = null)
-        {
-            formats ??= Helper.AllFormats;
+    internal void RunBinary(IEnumerable images, Func func,
+        Enums.BandFormat[] formats = null)
+    {
+        formats ??= Helper.AllFormats;
 
-            foreach (var x in images)
+        foreach (var x in images)
+        {
+            foreach (var y in formats)
             {
-                foreach (var y in formats)
+                foreach (var z in formats)
                 {
-                    foreach (var z in formats)
-                    {
-                        RunImageBinary(x.Cast(y), x.Cast(z), func);
-                    }
+                    RunImageBinary(x.Cast(y), x.Cast(z), func);
                 }
             }
         }
+    }
 
-        #endregion
+    #endregion
 
-        #region test overloadable operators
+    #region test overloadable operators
 
-        [Fact]
-        public void TestAdd()
+    [Fact]
+    public void TestAdd()
+    {
+        dynamic Add(dynamic x, dynamic y)
         {
-            dynamic Add(dynamic x, dynamic y)
-            {
-                return x + y;
-            }
-
-            RunArithConst(Add);
-            RunArith(Add);
+            return x + y;
         }
 
-        [Fact]
-        public void TestSub()
-        {
-            dynamic Sub(dynamic x, dynamic y)
-            {
-                return x - y;
-            }
+        RunArithConst(Add);
+        RunArith(Add);
+    }
 
-            RunArithConst(Sub);
-            RunArith(Sub);
+    [Fact]
+    public void TestSub()
+    {
+        dynamic Sub(dynamic x, dynamic y)
+        {
+            return x - y;
         }
 
-        [Fact]
-        public void TestMul()
-        {
-            dynamic Mul(dynamic x, dynamic y)
-            {
-                return x * y;
-            }
+        RunArithConst(Sub);
+        RunArith(Sub);
+    }
 
-            RunArithConst(Mul);
-            RunArith(Mul);
+    [Fact]
+    public void TestMul()
+    {
+        dynamic Mul(dynamic x, dynamic y)
+        {
+            return x * y;
         }
 
-        [Fact]
-        public void TestDiv()
-        {
-            dynamic Div(dynamic x, dynamic y)
-            {
-                return x / y;
-            }
+        RunArithConst(Mul);
+        RunArith(Mul);
+    }
 
-            // (const / image) needs (image ** -1), which won't work for complex
-            RunArithConst(Div, Helper.NonComplexFormats);
-            RunArith(Div);
+    [Fact]
+    public void TestDiv()
+    {
+        dynamic Div(dynamic x, dynamic y)
+        {
+            return x / y;
         }
 
-        [Fact]
-        public void TestFloorDiv()
+        // (const / image) needs (image ** -1), which won't work for complex
+        RunArithConst(Div, Helper.NonComplexFormats);
+        RunArith(Div);
+    }
+
+    [Fact]
+    public void TestFloorDiv()
+    {
+        dynamic FloorDiv(dynamic x, dynamic y)
         {
-            dynamic FloorDiv(dynamic x, dynamic y)
+            if (y is Image rightImage && x is not Image)
             {
-                if (y is Image rightImage && x is not Image)
-                {
-                    // There's no  __rfloordiv__ & __pow__ equivalent in C# :(
-                    return (rightImage.Pow(-1) * x).Floor();
-                }
-
-                if (x is Image leftImage)
-                {
-                    // There's no  __floordiv__ equivalent in C# :(
-                    return (leftImage / y as Image)?.Floor();
-                }
-
-                return Math.Floor(x / y);
+                // There's no  __rfloordiv__ & __pow__ equivalent in C# :(
+                return (rightImage.Pow(-1) * x).Floor();
             }
 
-            // (const // image) needs (image ** -1), which won't work for complex
-            RunArithConst(FloorDiv, Helper.NonComplexFormats);
-            RunArith(FloorDiv, Helper.NonComplexFormats);
-        }
-
-        [Fact]
-        public void TestPow()
-        {
-            dynamic Pow(dynamic x, dynamic y)
+            if (x is Image leftImage)
             {
-                if (y is Image rightImage && x is not Image)
-                {
-                    // There's no  __rpow__ equivalent in C# :(
-                    return rightImage.Wop(x);
-                }
-
-                if (x is Image leftImage)
-                {
-                    // There's no  __pow__ equivalent in C# :(
-                    return leftImage.Pow(y);
-                }
-
-                return Math.Pow(x, y);
+                // There's no  __floordiv__ equivalent in C# :(
+                return (leftImage / y as Image)?.Floor();
             }
 
-            // (image ** x) won't work for complex images ... just test non-complex
-            RunArithConst(Pow, Helper.NonComplexFormats);
-            RunArith(Pow, Helper.NonComplexFormats);
+            return Math.Floor(x / y);
         }
 
-        [Fact]
-        public void TestAnd()
+        // (const // image) needs (image ** -1), which won't work for complex
+        RunArithConst(FloorDiv, Helper.NonComplexFormats);
+        RunArith(FloorDiv, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestPow()
+    {
+        dynamic Pow(dynamic x, dynamic y)
         {
-            dynamic And(dynamic x, dynamic y)
+            if (y is Image rightImage && x is not Image)
             {
-                // C# doesn't allow bitwise AND on doubles
-                if (x is double dblX)
-                {
-                    x = (int)dblX;
-                }
-
-                if (y is double dblY)
-                {
-                    y = (int)dblY;
-                }
+                // There's no  __rpow__ equivalent in C# :(
+                return rightImage.Wop(x);
+            }
 
-                return x & y;
+            if (x is Image leftImage)
+            {
+                // There's no  __pow__ equivalent in C# :(
+                return leftImage.Pow(y);
             }
 
-            RunArithConst(And, Helper.NonComplexFormats);
-            RunArith(And, Helper.NonComplexFormats);
+            return Math.Pow(x, y);
         }
 
-        [Fact]
-        public void TestOr()
+        // (image ** x) won't work for complex images ... just test non-complex
+        RunArithConst(Pow, Helper.NonComplexFormats);
+        RunArith(Pow, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestAnd()
+    {
+        dynamic And(dynamic x, dynamic y)
         {
-            dynamic Or(dynamic x, dynamic y)
+            // C# doesn't allow bitwise AND on doubles
+            if (x is double dblX)
             {
-                // C# doesn't allow bitwise OR on doubles
-                if (x is double dblX)
-                {
-                    x = (int)dblX;
-                }
-
-                if (y is double dblY)
-                {
-                    y = (int)dblY;
-                }
+                x = (int)dblX;
+            }
 
-                return x | y;
+            if (y is double dblY)
+            {
+                y = (int)dblY;
             }
 
-            RunArithConst(Or, Helper.NonComplexFormats);
-            RunArith(Or, Helper.NonComplexFormats);
+            return x & y;
         }
 
-        [Fact]
-        public void TestXor()
+        RunArithConst(And, Helper.NonComplexFormats);
+        RunArith(And, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestOr()
+    {
+        dynamic Or(dynamic x, dynamic y)
         {
-            dynamic Xor(dynamic x, dynamic y)
+            // C# doesn't allow bitwise OR on doubles
+            if (x is double dblX)
             {
-                // C# doesn't allow bitwise exclusive-OR on doubles
-                if (x is double dblX)
-                {
-                    x = (int)dblX;
-                }
-
-                if (y is double dblY)
-                {
-                    y = (int)dblY;
-                }
+                x = (int)dblX;
+            }
 
-                return x ^ y;
+            if (y is double dblY)
+            {
+                y = (int)dblY;
             }
 
-            RunArithConst(Xor, Helper.NonComplexFormats);
-            RunArith(Xor, Helper.NonComplexFormats);
+            return x | y;
         }
 
-        [Fact]
-        public void TestMore()
+        RunArithConst(Or, Helper.NonComplexFormats);
+        RunArith(Or, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestXor()
+    {
+        dynamic Xor(dynamic x, dynamic y)
         {
-            dynamic More(dynamic x, dynamic y)
+            // C# doesn't allow bitwise exclusive-OR on doubles
+            if (x is double dblX)
             {
-                if (y is Image || x is Image)
-                {
-                    return x > y;
-                }
+                x = (int)dblX;
+            }
 
-                return x > y ? 255 : 0;
+            if (y is double dblY)
+            {
+                y = (int)dblY;
             }
 
-            RunArithConst(More);
-            RunArith(More);
+            return x ^ y;
         }
 
-        [Fact]
-        public void TestMoreEq()
+        RunArithConst(Xor, Helper.NonComplexFormats);
+        RunArith(Xor, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestMore()
+    {
+        dynamic More(dynamic x, dynamic y)
         {
-            dynamic MoreEq(dynamic x, dynamic y)
+            if (y is Image || x is Image)
             {
-                if (y is Image || x is Image)
-                {
-                    return x >= y;
-                }
-
-                return x >= y ? 255 : 0;
+                return x > y;
             }
 
-            RunArithConst(MoreEq);
-            RunArith(MoreEq);
+            return x > y ? 255 : 0;
         }
 
-        [Fact]
-        public void TestLess()
+        RunArithConst(More);
+        RunArith(More);
+    }
+
+    [Fact]
+    public void TestMoreEq()
+    {
+        dynamic MoreEq(dynamic x, dynamic y)
         {
-            dynamic Less(dynamic x, dynamic y)
+            if (y is Image || x is Image)
             {
-                if (y is Image || x is Image)
-                {
-                    return x < y;
-                }
-
-                return x < y ? 255 : 0;
+                return x >= y;
             }
 
-            RunArithConst(Less);
-            RunArith(Less);
+            return x >= y ? 255 : 0;
         }
 
-        [Fact]
-        public void TestLessEq()
+        RunArithConst(MoreEq);
+        RunArith(MoreEq);
+    }
+
+    [Fact]
+    public void TestLess()
+    {
+        dynamic Less(dynamic x, dynamic y)
         {
-            dynamic LessEq(dynamic x, dynamic y)
+            if (y is Image || x is Image)
             {
-                if (y is Image || x is Image)
-                {
-                    return x <= y;
-                }
-
-                return x <= y ? 255 : 0;
+                return x < y;
             }
 
-            RunArithConst(LessEq);
-            RunArith(LessEq);
+            return x < y ? 255 : 0;
         }
 
-        [Fact]
-        public void TestEqual()
+        RunArithConst(Less);
+        RunArith(Less);
+    }
+
+    [Fact]
+    public void TestLessEq()
+    {
+        dynamic LessEq(dynamic x, dynamic y)
         {
-            dynamic Equal(dynamic x, dynamic y)
+            if (y is Image || x is Image)
             {
-                if (y is Image rightImage && x is not Image)
-                {
-                    return x == rightImage;
-                }
-
-                if (x is Image leftImage && y is not Image)
-                {
-                    return y == leftImage;
-                }
-
-                if (y is Image)
-                {
-                    return y.Equal(x);
-                }
-
-                return x == y ? 255 : 0;
+                return x <= y;
             }
 
-            RunArithConst(Equal);
-            RunArith(Equal);
+            return x <= y ? 255 : 0;
         }
 
-        [Fact]
-        public void TestNotEq()
+        RunArithConst(LessEq);
+        RunArith(LessEq);
+    }
+
+    [Fact]
+    public void TestEqual()
+    {
+        dynamic Equal(dynamic x, dynamic y)
         {
-            dynamic NotEq(dynamic x, dynamic y)
+            if (y is Image rightImage && x is not Image)
             {
-                if (y is Image rightImage && x is not Image)
-                {
-                    return x != rightImage;
-                }
-
-                if (x is Image leftImage && y is not Image)
-                {
-                    return y != leftImage;
-                }
-
-                if (y is Image)
-                {
-                    return y.NotEqual(x);
-                }
-
-                return x != y ? 255 : 0;
+                return x == rightImage;
             }
 
-            RunArithConst(NotEq);
-            RunArith(NotEq);
-
-            if (NetVips.AtLeastLibvips(8, 9))
+            if (x is Image leftImage && y is not Image)
             {
-                // comparisons against out of range values should always fail, and
-                // comparisons to fractional values should always fail
-                var z = Image.Grey(256, 256, uchar: true);
+                return y == leftImage;
+            }
 
-                Assert.Equal(0, z.Equal(1000).Max());
-                Assert.Equal(255, z.Equal(12).Max());
-                Assert.Equal(0, z.Equal(12.5).Max());
+            if (y is Image)
+            {
+                return y.Equal(x);
             }
+
+            return x == y ? 255 : 0;
         }
 
-        [Fact]
-        public void TestAbs()
+        RunArithConst(Equal);
+        RunArith(Equal);
+    }
+
+    [Fact]
+    public void TestNotEq()
+    {
+        dynamic NotEq(dynamic x, dynamic y)
         {
-            dynamic Abs(dynamic x)
+            if (y is Image rightImage && x is not Image)
             {
-                if (x is Image image)
-                {
-                    return image.Abs();
-                }
-
-                return Math.Abs(x);
+                return x != rightImage;
             }
 
-            var im = _colour * -1;
-            RunUnary(new[]
+            if (x is Image leftImage && y is not Image)
             {
-                im
-            }, Abs);
-        }
+                return y != leftImage;
+            }
 
-        [Fact]
-        public void TestLShift()
-        {
-            dynamic LShift(dynamic x)
+            if (y is Image)
             {
-                // C# doesn't allow lshift on doubles
-                if (x is double dblX)
-                {
-                    x = (int)dblX;
-                }
-
-                return x << 2;
+                return y.NotEqual(x);
             }
 
-            // we don't support constant << image, treat as a unary
-            RunUnary(_allImages, LShift, Helper.NonComplexFormats);
+            return x != y ? 255 : 0;
         }
 
-        [Fact]
-        public void TestRShift()
-        {
-            dynamic RShift(dynamic x)
-            {
-                // C# doesn't allow rshift on doubles
-                if (x is double dblX)
-                {
-                    x = (int)dblX;
-                }
+        RunArithConst(NotEq);
+        RunArith(NotEq);
 
-                return x >> 2;
-            }
+        if (NetVips.AtLeastLibvips(8, 9))
+        {
+            // comparisons against out of range values should always fail, and
+            // comparisons to fractional values should always fail
+            var z = Image.Grey(256, 256, uchar: true);
 
-            // we don't support constant >> image, treat as a unary
-            RunUnary(_allImages, RShift, Helper.NonComplexFormats);
+            Assert.Equal(0, z.Equal(1000).Max());
+            Assert.Equal(255, z.Equal(12).Max());
+            Assert.Equal(0, z.Equal(12.5).Max());
         }
+    }
 
-        [Fact]
-        public void TestMod()
+    [Fact]
+    public void TestAbs()
+    {
+        dynamic Abs(dynamic x)
         {
-            dynamic Mod(dynamic x)
+            if (x is Image image)
             {
-                return x % 2;
+                return image.Abs();
             }
 
-            // we don't support constant % image, treat as a unary
-            RunUnary(_allImages, Mod, Helper.NonComplexFormats);
+            return Math.Abs(x);
         }
 
-        [Fact]
-        public void TestPos()
+        var im = _colour * -1;
+        RunUnary(new[]
+        {
+            im
+        }, Abs);
+    }
+
+    [Fact]
+    public void TestLShift()
+    {
+        dynamic LShift(dynamic x)
         {
-            dynamic Pos(dynamic x)
+            // C# doesn't allow lshift on doubles
+            if (x is double dblX)
             {
-                return x;
+                x = (int)dblX;
             }
 
-            RunUnary(_allImages, Pos);
+            return x << 2;
         }
 
-        [Fact]
-        public void TestNeg()
+        // we don't support constant << image, treat as a unary
+        RunUnary(_allImages, LShift, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestRShift()
+    {
+        dynamic RShift(dynamic x)
         {
-            dynamic Neg(dynamic x)
+            // C# doesn't allow rshift on doubles
+            if (x is double dblX)
             {
-                return x * -1;
+                x = (int)dblX;
             }
 
-            RunUnary(_allImages, Neg);
+            return x >> 2;
         }
 
-        [Fact]
-        public void TestInvert()
+        // we don't support constant >> image, treat as a unary
+        RunUnary(_allImages, RShift, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestMod()
+    {
+        dynamic Mod(dynamic x)
         {
-            dynamic Invert(dynamic x)
-            {
-                if (x is double dblX)
-                {
-                    x = (int)dblX;
-                }
+            return x % 2;
+        }
 
-                return (x ^ -1) & 0xff;
-            }
+        // we don't support constant % image, treat as a unary
+        RunUnary(_allImages, Mod, Helper.NonComplexFormats);
+    }
 
-            // image ^ -1 is trimmed to image max so it's hard to test for all formats
-            // just test uchar
-            RunUnary(_allImages, Invert, new[] { Enums.BandFormat.Uchar });
+    [Fact]
+    public void TestPos()
+    {
+        dynamic Pos(dynamic x)
+        {
+            return x;
         }
 
-        #endregion
-
-        #region test the rest of VipsArithmetic
+        RunUnary(_allImages, Pos);
+    }
 
-        [Fact]
-        public void TestAvg()
+    [Fact]
+    public void TestNeg()
+    {
+        dynamic Neg(dynamic x)
         {
-            var im = Image.Black(50, 100);
-            var test = im.Insert(im + 100, 50, 0, expand: true);
-
-            foreach (var fmt in Helper.AllFormats)
-            {
-                Assert.Equal(50, test.Cast(fmt).Avg());
-            }
+            return x * -1;
         }
 
-        [Fact]
-        public void TestDeviate()
-        {
-            var im = Image.Black(50, 100);
-            var test = im.Insert(im + 100, 50, 0, expand: true);
+        RunUnary(_allImages, Neg);
+    }
 
-            foreach (var fmt in Helper.NonComplexFormats)
+    [Fact]
+    public void TestInvert()
+    {
+        dynamic Invert(dynamic x)
+        {
+            if (x is double dblX)
             {
-                Assert.Equal(50, test.Cast(fmt).Deviate(), 2);
+                x = (int)dblX;
             }
+
+            return (x ^ -1) & 0xff;
         }
 
-        [Fact]
-        public void TestPolar()
-        {
-            var im = Image.Black(100, 100) + 100;
-            im = im.Complexform(im);
+        // image ^ -1 is trimmed to image max so it's hard to test for all formats
+        // just test uchar
+        RunUnary(_allImages, Invert, new[] { Enums.BandFormat.Uchar });
+    }
 
-            im = im.Polar();
+    #endregion
 
-            Assert.Equal(100 * Math.Pow(2, 0.5), im.Real().Avg(), 4);
-            Assert.Equal(45, im.Imag().Avg());
-        }
+    #region test the rest of VipsArithmetic
 
-        [Fact]
-        public void TestRect()
-        {
-            var im = Image.Black(100, 100);
-            im = (im + 100 * Math.Pow(2, 0.5)).Complexform(im + 45);
-            im = im.Rect();
+    [Fact]
+    public void TestAvg()
+    {
+        var im = Image.Black(50, 100);
+        var test = im.Insert(im + 100, 50, 0, expand: true);
 
-            Assert.Equal(100, im.Real().Avg());
-            Assert.Equal(100, im.Imag().Avg());
+        foreach (var fmt in Helper.AllFormats)
+        {
+            Assert.Equal(50, test.Cast(fmt).Avg());
         }
+    }
+
+    [Fact]
+    public void TestDeviate()
+    {
+        var im = Image.Black(50, 100);
+        var test = im.Insert(im + 100, 50, 0, expand: true);
 
-        [Fact]
-        public void TestConjugate()
+        foreach (var fmt in Helper.NonComplexFormats)
         {
-            var im = Image.Black(100, 100) + 100;
-            im = im.Complexform(im);
+            Assert.Equal(50, test.Cast(fmt).Deviate(), 2);
+        }
+    }
 
-            im = im.Conj();
+    [Fact]
+    public void TestPolar()
+    {
+        var im = Image.Black(100, 100) + 100;
+        im = im.Complexform(im);
 
-            Assert.Equal(100, im.Real().Avg());
-            Assert.Equal(-100, im.Imag().Avg());
-        }
+        im = im.Polar();
 
-        [Fact]
-        public void TestHistFind()
-        {
-            var im = Image.Black(50, 100);
-            var test = im.Insert(im + 10, 50, 0, expand: true);
+        Assert.Equal(100 * Math.Pow(2, 0.5), im.Real().Avg(), 4);
+        Assert.Equal(45, im.Imag().Avg());
+    }
 
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var hist = test.Cast(fmt).HistFind();
-                Assert.Equal(new double[] { 5000 }, hist[0, 0]);
-                Assert.Equal(new double[] { 5000 }, hist[10, 0]);
-                Assert.Equal(new double[] { 0 }, hist[5, 0]);
-            }
+    [Fact]
+    public void TestRect()
+    {
+        var im = Image.Black(100, 100);
+        im = (im + 100 * Math.Pow(2, 0.5)).Complexform(im + 45);
+        im = im.Rect();
 
-            test *= new[] { 1, 2, 3 };
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var hist = test.Cast(fmt).HistFind(band: 0);
-                Assert.Equal(new double[] { 5000 }, hist[0, 0]);
-                Assert.Equal(new double[] { 5000 }, hist[10, 0]);
-                Assert.Equal(new double[] { 0 }, hist[5, 0]);
-
-                hist = test.Cast(fmt).HistFind(band: 1);
-                Assert.Equal(new double[] { 5000 }, hist[0, 0]);
-                Assert.Equal(new double[] { 5000 }, hist[20, 0]);
-                Assert.Equal(new double[] { 0 }, hist[5, 0]);
-            }
+        Assert.Equal(100, im.Real().Avg());
+        Assert.Equal(100, im.Imag().Avg());
+    }
+
+    [Fact]
+    public void TestConjugate()
+    {
+        var im = Image.Black(100, 100) + 100;
+        im = im.Complexform(im);
+
+        im = im.Conj();
+
+        Assert.Equal(100, im.Real().Avg());
+        Assert.Equal(-100, im.Imag().Avg());
+    }
+
+    [Fact]
+    public void TestHistFind()
+    {
+        var im = Image.Black(50, 100);
+        var test = im.Insert(im + 10, 50, 0, expand: true);
+
+        foreach (var fmt in Helper.AllFormats)
+        {
+            var hist = test.Cast(fmt).HistFind();
+            Assert.Equal(new double[] { 5000 }, hist[0, 0]);
+            Assert.Equal(new double[] { 5000 }, hist[10, 0]);
+            Assert.Equal(new double[] { 0 }, hist[5, 0]);
         }
 
-        [Fact]
-        public void TestHistFindIndexed()
+        test *= new[] { 1, 2, 3 };
+        foreach (var fmt in Helper.AllFormats)
         {
-            var im = Image.Black(50, 100);
-            var test = im.Insert(im + 10, 50, 0, expand: true);
+            var hist = test.Cast(fmt).HistFind(band: 0);
+            Assert.Equal(new double[] { 5000 }, hist[0, 0]);
+            Assert.Equal(new double[] { 5000 }, hist[10, 0]);
+            Assert.Equal(new double[] { 0 }, hist[5, 0]);
 
-            // There's no  __floordiv__ equivalent in C# :(
-            var index = (test / 10).Floor();
+            hist = test.Cast(fmt).HistFind(band: 1);
+            Assert.Equal(new double[] { 5000 }, hist[0, 0]);
+            Assert.Equal(new double[] { 5000 }, hist[20, 0]);
+            Assert.Equal(new double[] { 0 }, hist[5, 0]);
+        }
+    }
+
+    [Fact]
+    public void TestHistFindIndexed()
+    {
+        var im = Image.Black(50, 100);
+        var test = im.Insert(im + 10, 50, 0, expand: true);
 
-            foreach (var x in Helper.NonComplexFormats)
+        // There's no  __floordiv__ equivalent in C# :(
+        var index = (test / 10).Floor();
+
+        foreach (var x in Helper.NonComplexFormats)
+        {
+            foreach (var y in new[] { Enums.BandFormat.Uchar, Enums.BandFormat.Ushort })
             {
-                foreach (var y in new[] { Enums.BandFormat.Uchar, Enums.BandFormat.Ushort })
-                {
-                    var a = test.Cast(x);
-                    var b = index.Cast(y);
-                    var hist = a.HistFindIndexed(b);
-                    Assert.Equal(new double[] { 0 }, hist[0, 0]);
-                    Assert.Equal(new double[] { 50000 }, hist[1, 0]);
-                }
+                var a = test.Cast(x);
+                var b = index.Cast(y);
+                var hist = a.HistFindIndexed(b);
+                Assert.Equal(new double[] { 0 }, hist[0, 0]);
+                Assert.Equal(new double[] { 50000 }, hist[1, 0]);
             }
         }
+    }
 
-        [Fact]
-        public void TestHistFindNdim()
-        {
-            var im = Image.Black(100, 100) + new[] { 1, 2, 3 };
+    [Fact]
+    public void TestHistFindNdim()
+    {
+        var im = Image.Black(100, 100) + new[] { 1, 2, 3 };
 
-            foreach (var fmt in Helper.NonComplexFormats)
-            {
-                var hist = im.Cast(fmt).HistFindNdim();
+        foreach (var fmt in Helper.NonComplexFormats)
+        {
+            var hist = im.Cast(fmt).HistFindNdim();
 
-                Assert.Equal(10000, hist[0, 0][0]);
-                Assert.Equal(0, hist[5, 5][5]);
+            Assert.Equal(10000, hist[0, 0][0]);
+            Assert.Equal(0, hist[5, 5][5]);
 
-                hist = im.Cast(fmt).HistFindNdim(bins: 1);
+            hist = im.Cast(fmt).HistFindNdim(bins: 1);
 
-                Assert.Equal(10000, hist[0, 0][0]);
-                Assert.Equal(1, hist.Width);
-                Assert.Equal(1, hist.Height);
-                Assert.Equal(1, hist.Bands);
-            }
+            Assert.Equal(10000, hist[0, 0][0]);
+            Assert.Equal(1, hist.Width);
+            Assert.Equal(1, hist.Height);
+            Assert.Equal(1, hist.Bands);
         }
+    }
 
-        [Fact]
-        public void TestHoughCircle()
-        {
-            var test = Image.Black(100, 100).Mutate(x => x.DrawCircle(new double[] { 100 }, 50, 50, 40));
+    [Fact]
+    public void TestHoughCircle()
+    {
+        var test = Image.Black(100, 100).Mutate(x => x.DrawCircle(new double[] { 100 }, 50, 50, 40));
 
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var im = test.Cast(fmt);
-                var hough = im.HoughCircle(minRadius: 35, maxRadius: 45);
+        foreach (var fmt in Helper.AllFormats)
+        {
+            var im = test.Cast(fmt);
+            var hough = im.HoughCircle(minRadius: 35, maxRadius: 45);
 
-                var maxPos = hough.MaxPos();
-                var v = maxPos[0];
-                var x = (int)maxPos[1];
-                var y = (int)maxPos[2];
+            var maxPos = hough.MaxPos();
+            var v = maxPos[0];
+            var x = (int)maxPos[1];
+            var y = (int)maxPos[2];
 
-                var vec = hough[x, y];
-                var r = Array.IndexOf(vec, vec.Min(_ => v)) + 35;
+            var vec = hough[x, y];
+            var r = Array.IndexOf(vec, vec.Min(_ => v)) + 35;
 
-                Assert.Equal(50, x);
-                Assert.Equal(50, y);
-                Assert.Equal(40, r);
-            }
+            Assert.Equal(50, x);
+            Assert.Equal(50, y);
+            Assert.Equal(40, r);
         }
+    }
 
-        [SkippableFact]
-        public void TestHoughLine()
-        {
-            // hough_line changed the way it codes parameter space in 8.7 ... don't
-            // test earlier versions
-            Skip.IfNot(NetVips.AtLeastLibvips(8, 7), "requires libvips >= 8.7");
+    [SkippableFact]
+    public void TestHoughLine()
+    {
+        // hough_line changed the way it codes parameter space in 8.7 ... don't
+        // test earlier versions
+        Skip.IfNot(NetVips.AtLeastLibvips(8, 7), "requires libvips >= 8.7");
 
-            var test = Image.Black(100, 100).Mutate(x => x.DrawLine(new double[] { 100 }, 10, 90, 90, 10));
+        var test = Image.Black(100, 100).Mutate(x => x.DrawLine(new double[] { 100 }, 10, 90, 90, 10));
 
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var im = test.Cast(fmt);
-                var hough = im.HoughLine();
+        foreach (var fmt in Helper.AllFormats)
+        {
+            var im = test.Cast(fmt);
+            var hough = im.HoughLine();
 
-                var maxPos = hough.MaxPos();
-                var x = maxPos[1];
-                var y = maxPos[2];
+            var maxPos = hough.MaxPos();
+            var x = maxPos[1];
+            var y = maxPos[2];
 
-                var angle = Math.Floor(180.0 * x / hough.Width);
-                var distance = Math.Floor(test.Height * y / hough.Height);
+            var angle = Math.Floor(180.0 * x / hough.Width);
+            var distance = Math.Floor(test.Height * y / hough.Height);
 
-                Assert.Equal(45, angle);
-                Assert.Equal(70, distance);
-            }
+            Assert.Equal(45, angle);
+            Assert.Equal(70, distance);
         }
+    }
 
-        [Fact]
-        public void TestSin()
+    [Fact]
+    public void TestSin()
+    {
+        dynamic Sin(dynamic x)
         {
-            dynamic Sin(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.Sin();
-                }
-
-                return Math.Sin(Math.PI / 180 * x);
+                return image.Sin();
             }
 
-            RunUnary(_allImages, Sin, Helper.NonComplexFormats);
+            return Math.Sin(Math.PI / 180 * x);
         }
 
-        [Fact]
-        public void TestCos()
+        RunUnary(_allImages, Sin, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestCos()
+    {
+        dynamic Cos(dynamic x)
         {
-            dynamic Cos(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.Cos();
-                }
-
-                return Math.Cos(Math.PI / 180 * x);
+                return image.Cos();
             }
 
-            RunUnary(_allImages, Cos, Helper.NonComplexFormats);
+            return Math.Cos(Math.PI / 180 * x);
         }
 
-        [Fact]
-        public void TestTan()
+        RunUnary(_allImages, Cos, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestTan()
+    {
+        dynamic Tan(dynamic x)
         {
-            dynamic Tan(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.Tan();
-                }
-
-                return Math.Tan(Math.PI / 180 * x);
+                return image.Tan();
             }
 
-            RunUnary(_allImages, Tan, Helper.NonComplexFormats);
+            return Math.Tan(Math.PI / 180 * x);
         }
 
-        [Fact]
-        public void TestAsin()
+        RunUnary(_allImages, Tan, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestAsin()
+    {
+        dynamic Asin(dynamic x)
         {
-            dynamic Asin(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.Asin();
-                }
-
-                return Math.Asin(x) * (180.0 / Math.PI);
+                return image.Asin();
             }
 
-            var im = (Image.Black(100, 100) + new[] { 1, 2, 3 }) / 3.0;
-            RunUnary(new[] { im }, Asin, Helper.NonComplexFormats);
+            return Math.Asin(x) * (180.0 / Math.PI);
         }
 
-        [Fact]
-        public void TestAcos()
+        var im = (Image.Black(100, 100) + new[] { 1, 2, 3 }) / 3.0;
+        RunUnary(new[] { im }, Asin, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestAcos()
+    {
+        dynamic Acos(dynamic x)
         {
-            dynamic Acos(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.Acos();
-                }
-
-                return Math.Acos(x) * (180.0 / Math.PI);
+                return image.Acos();
             }
 
-            var im = (Image.Black(100, 100) + new[] { 1, 2, 3 }) / 3.0;
-            RunUnary(new[] { im }, Acos, Helper.NonComplexFormats);
+            return Math.Acos(x) * (180.0 / Math.PI);
         }
 
-        [Fact]
-        public void TestAtan()
+        var im = (Image.Black(100, 100) + new[] { 1, 2, 3 }) / 3.0;
+        RunUnary(new[] { im }, Acos, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestAtan()
+    {
+        dynamic Atan(dynamic x)
         {
-            dynamic Atan(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.Atan();
-                }
-
-                return Math.Atan(x) * (180.0 / Math.PI);
+                return image.Atan();
             }
 
-            var im = (Image.Black(100, 100) + new[] { 1, 2, 3 }) / 3.0;
-            RunUnary(new[] { im }, Atan, Helper.NonComplexFormats);
+            return Math.Atan(x) * (180.0 / Math.PI);
         }
 
-        [SkippableFact]
-        public void TestSinh()
-        {
-            Skip.IfNot(NetVips.AtLeastLibvips(8, 12), "requires libvips >= 8.12");
+        var im = (Image.Black(100, 100) + new[] { 1, 2, 3 }) / 3.0;
+        RunUnary(new[] { im }, Atan, Helper.NonComplexFormats);
+    }
 
-            dynamic Sinh(dynamic x)
-            {
-                if (x is Image image)
-                {
-                    return image.Sinh();
-                }
+    [SkippableFact]
+    public void TestSinh()
+    {
+        Skip.IfNot(NetVips.AtLeastLibvips(8, 12), "requires libvips >= 8.12");
 
-                return Math.Sinh(x);
+        dynamic Sinh(dynamic x)
+        {
+            if (x is Image image)
+            {
+                return image.Sinh();
             }
 
-            RunUnary(_allImages, Sinh, Helper.NonComplexFormats);
+            return Math.Sinh(x);
         }
 
-        [SkippableFact]
-        public void TestCosh()
-        {
-            Skip.IfNot(NetVips.AtLeastLibvips(8, 12), "requires libvips >= 8.12");
+        RunUnary(_allImages, Sinh, Helper.NonComplexFormats);
+    }
 
-            dynamic Cosh(dynamic x)
-            {
-                if (x is Image image)
-                {
-                    return image.Cosh();
-                }
+    [SkippableFact]
+    public void TestCosh()
+    {
+        Skip.IfNot(NetVips.AtLeastLibvips(8, 12), "requires libvips >= 8.12");
 
-                return Math.Cosh(x);
+        dynamic Cosh(dynamic x)
+        {
+            if (x is Image image)
+            {
+                return image.Cosh();
             }
 
-            RunUnary(_allImages, Cosh, Helper.NonComplexFormats);
+            return Math.Cosh(x);
         }
 
-        [SkippableFact]
-        public void TestTanh()
-        {
-            Skip.IfNot(NetVips.AtLeastLibvips(8, 12), "requires libvips >= 8.12");
+        RunUnary(_allImages, Cosh, Helper.NonComplexFormats);
+    }
 
-            dynamic Tanh(dynamic x)
-            {
-                if (x is Image image)
-                {
-                    return image.Tanh();
-                }
+    [SkippableFact]
+    public void TestTanh()
+    {
+        Skip.IfNot(NetVips.AtLeastLibvips(8, 12), "requires libvips >= 8.12");
 
-                return Math.Tanh(x);
+        dynamic Tanh(dynamic x)
+        {
+            if (x is Image image)
+            {
+                return image.Tanh();
             }
 
-            RunUnary(_allImages, Tanh, Helper.NonComplexFormats);
+            return Math.Tanh(x);
         }
 
+        RunUnary(_allImages, Tanh, Helper.NonComplexFormats);
+    }
+
 #if NET5_0_OR_GREATER // Inverse hyperbolic functions are not available on Mono
-        [SkippableFact]
-        public void TestAsinh()
-        {
-            Skip.IfNot(NetVips.AtLeastLibvips(8, 12), "requires libvips >= 8.12");
+    [SkippableFact]
+    public void TestAsinh()
+    {
+        Skip.IfNot(NetVips.AtLeastLibvips(8, 12), "requires libvips >= 8.12");
 
-            dynamic Asinh(dynamic x)
+        dynamic Asinh(dynamic x)
+        {
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.Asinh();
-                }
-
-                return Math.Asinh(x);
+                return image.Asinh();
             }
 
-            var im = (Image.Black(100, 100) + new[] { 4, 5, 6 }) / 3.0;
-            RunUnary(new[] { im }, Asinh, Helper.NonComplexFormats);
+            return Math.Asinh(x);
         }
 
-        [SkippableFact]
-        public void TestAcosh()
-        {
-            Skip.IfNot(NetVips.AtLeastLibvips(8, 12), "requires libvips >= 8.12");
+        var im = (Image.Black(100, 100) + new[] { 4, 5, 6 }) / 3.0;
+        RunUnary(new[] { im }, Asinh, Helper.NonComplexFormats);
+    }
 
-            dynamic Acosh(dynamic x)
-            {
-                if (x is Image image)
-                {
-                    return image.Acosh();
-                }
+    [SkippableFact]
+    public void TestAcosh()
+    {
+        Skip.IfNot(NetVips.AtLeastLibvips(8, 12), "requires libvips >= 8.12");
 
-                return Math.Acosh(x);
+        dynamic Acosh(dynamic x)
+        {
+            if (x is Image image)
+            {
+                return image.Acosh();
             }
 
-            var im = (Image.Black(100, 100) + new[] { 4, 5, 6 }) / 3.0;
-            RunUnary(new[] { im }, Acosh, Helper.NonComplexFormats);
+            return Math.Acosh(x);
         }
 
-        [SkippableFact]
-        public void TestAtanh()
-        {
-            Skip.IfNot(NetVips.AtLeastLibvips(8, 12), "requires libvips >= 8.12");
+        var im = (Image.Black(100, 100) + new[] { 4, 5, 6 }) / 3.0;
+        RunUnary(new[] { im }, Acosh, Helper.NonComplexFormats);
+    }
 
-            dynamic Atanh(dynamic x)
-            {
-                if (x is Image image)
-                {
-                    return image.Atanh();
-                }
+    [SkippableFact]
+    public void TestAtanh()
+    {
+        Skip.IfNot(NetVips.AtLeastLibvips(8, 12), "requires libvips >= 8.12");
 
-                return Math.Atanh(x);
+        dynamic Atanh(dynamic x)
+        {
+            if (x is Image image)
+            {
+                return image.Atanh();
             }
 
-            var im = (Image.Black(100, 100) + new[] { 0, 1, 2 }) / 3.0;
-            RunUnary(new[] { im }, Atanh, Helper.NonComplexFormats);
+            return Math.Atanh(x);
         }
+
+        var im = (Image.Black(100, 100) + new[] { 0, 1, 2 }) / 3.0;
+        RunUnary(new[] { im }, Atanh, Helper.NonComplexFormats);
+    }
 #endif
 
-        [SkippableFact]
-        public void TestAtan2()
-        {
-            Skip.IfNot(NetVips.AtLeastLibvips(8, 12), "requires libvips >= 8.12");
+    [SkippableFact]
+    public void TestAtan2()
+    {
+        Skip.IfNot(NetVips.AtLeastLibvips(8, 12), "requires libvips >= 8.12");
 
-            dynamic Atan2(dynamic x, dynamic y)
+        dynamic Atan2(dynamic x, dynamic y)
+        {
+            if (x is Image left)
             {
-                if (x is Image left)
-                {
-                    return left.Atan2(y);
-                }
-
-                return Math.Atan2(x[0], y[0]) * (180.0 / Math.PI);
+                return left.Atan2(y);
             }
 
-            var im = (Image.Black(100, 100) + new[] { 0, 1, 2 }) / 3.0;
-            RunBinary(im.Bandsplit(), Atan2, Helper.NonComplexFormats);
+            return Math.Atan2(x[0], y[0]) * (180.0 / Math.PI);
         }
 
-        [Fact]
-        public void TestLog()
+        var im = (Image.Black(100, 100) + new[] { 0, 1, 2 }) / 3.0;
+        RunBinary(im.Bandsplit(), Atan2, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestLog()
+    {
+        dynamic Log(dynamic x)
         {
-            dynamic Log(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.Log();
-                }
-
-                return Math.Log(x);
+                return image.Log();
             }
 
-            RunUnary(_allImages, Log, Helper.NonComplexFormats);
+            return Math.Log(x);
         }
 
-        [Fact]
-        public void TestLog10()
+        RunUnary(_allImages, Log, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestLog10()
+    {
+        dynamic Log10(dynamic x)
         {
-            dynamic Log10(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.Log10();
-                }
-
-                return Math.Log10(x);
+                return image.Log10();
             }
 
-            RunUnary(_allImages, Log10, Helper.NonComplexFormats);
+            return Math.Log10(x);
         }
 
-        [Fact]
-        public void TestExp()
+        RunUnary(_allImages, Log10, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestExp()
+    {
+        dynamic Exp(dynamic x)
         {
-            dynamic Exp(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.Exp();
-                }
-
-                return Math.Exp(x);
+                return image.Exp();
             }
 
-            RunUnary(_allImages, Exp, Helper.NonComplexFormats);
+            return Math.Exp(x);
         }
 
-        [Fact]
-        public void TestExp10()
+        RunUnary(_allImages, Exp, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestExp10()
+    {
+        dynamic Exp10(dynamic x)
         {
-            dynamic Exp10(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.Exp10();
-                }
-
-                return Math.Pow(10, x);
+                return image.Exp10();
             }
 
-            RunUnary(_allImages, Exp10, Helper.NonComplexFormats);
+            return Math.Pow(10, x);
         }
 
-        [Fact]
-        public void TestFloor()
+        RunUnary(_allImages, Exp10, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestFloor()
+    {
+        dynamic Floor(dynamic x)
         {
-            dynamic Floor(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.Floor();
-                }
-
-                return Math.Floor(x);
+                return image.Floor();
             }
 
-            RunUnary(_allImages, Floor, Helper.NonComplexFormats);
+            return Math.Floor(x);
         }
 
-        [Fact]
-        public void TestCeil()
+        RunUnary(_allImages, Floor, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestCeil()
+    {
+        dynamic Ceil(dynamic x)
         {
-            dynamic Ceil(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.Ceil();
-                }
-
-                return Math.Ceiling(x);
+                return image.Ceil();
             }
 
-            RunUnary(_allImages, Ceil, Helper.NonComplexFormats);
+            return Math.Ceiling(x);
         }
 
-        [Fact]
-        public void TestRint()
+        RunUnary(_allImages, Ceil, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestRint()
+    {
+        dynamic Rint(dynamic x)
         {
-            dynamic Rint(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.Rint();
-                }
-
-                return Math.Round(x);
+                return image.Rint();
             }
 
-            RunUnary(_allImages, Rint, Helper.NonComplexFormats);
+            return Math.Round(x);
         }
 
-        [Fact]
-        public void TestSign()
+        RunUnary(_allImages, Rint, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestSign()
+    {
+        dynamic Sign(dynamic x)
         {
-            dynamic Sign(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.Sign();
-                }
-
-                if (x > 0)
-                {
-                    return 1;
-                }
+                return image.Sign();
+            }
 
-                if (x < 0)
-                {
-                    return -1;
-                }
+            if (x > 0)
+            {
+                return 1;
+            }
 
-                return 0;
+            if (x < 0)
+            {
+                return -1;
             }
 
-            RunUnary(_allImages, Sign, Helper.NonComplexFormats);
+            return 0;
         }
 
-        [Fact]
-        public void TestMax()
-        {
-            var test = Image.Black(100, 100).Mutate(x => x.DrawRect(new double[] { 100 }, 40, 50, 1, 1));
+        RunUnary(_allImages, Sign, Helper.NonComplexFormats);
+    }
 
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var v = test.Cast(fmt).Max();
+    [Fact]
+    public void TestMax()
+    {
+        var test = Image.Black(100, 100).Mutate(x => x.DrawRect(new double[] { 100 }, 40, 50, 1, 1));
+
+        foreach (var fmt in Helper.AllFormats)
+        {
+            var v = test.Cast(fmt).Max();
 
-                Assert.Equal(100, v);
+            Assert.Equal(100, v);
 
-                var maxPos = test.Cast(fmt).MaxPos();
-                v = maxPos[0];
-                var x = maxPos[1];
-                var y = maxPos[2];
+            var maxPos = test.Cast(fmt).MaxPos();
+            v = maxPos[0];
+            var x = maxPos[1];
+            var y = maxPos[2];
 
-                Assert.Equal(100, v);
-                Assert.Equal(40, x);
-                Assert.Equal(50, y);
-            }
+            Assert.Equal(100, v);
+            Assert.Equal(40, x);
+            Assert.Equal(50, y);
         }
+    }
 
-        [Fact]
-        public void TestMin()
-        {
-            var test = (Image.Black(100, 100) + 100).Mutate(x => x.DrawRect(new double[] { 0 }, 40, 50, 1, 1));
+    [Fact]
+    public void TestMin()
+    {
+        var test = (Image.Black(100, 100) + 100).Mutate(x => x.DrawRect(new double[] { 0 }, 40, 50, 1, 1));
 
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var v = test.Cast(fmt).Min();
+        foreach (var fmt in Helper.AllFormats)
+        {
+            var v = test.Cast(fmt).Min();
 
-                Assert.Equal(0, v);
+            Assert.Equal(0, v);
 
-                var minPos = test.Cast(fmt).MinPos();
-                v = minPos[0];
-                var x = minPos[1];
-                var y = minPos[2];
+            var minPos = test.Cast(fmt).MinPos();
+            v = minPos[0];
+            var x = minPos[1];
+            var y = minPos[2];
 
-                Assert.Equal(0, v);
-                Assert.Equal(40, x);
-                Assert.Equal(50, y);
-            }
+            Assert.Equal(0, v);
+            Assert.Equal(40, x);
+            Assert.Equal(50, y);
         }
+    }
 
-        [Fact]
-        public void TestMeasure()
-        {
-            var im = Image.Black(50, 50);
-            var test = im.Insert(im + 10, 50, 0, expand: true);
+    [Fact]
+    public void TestMeasure()
+    {
+        var im = Image.Black(50, 50);
+        var test = im.Insert(im + 10, 50, 0, expand: true);
 
-            foreach (var fmt in Helper.NonComplexFormats)
-            {
-                var a = test.Cast(fmt);
-                var matrix = a.Measure(2, 1);
-                var p1 = matrix[0, 0][0];
-                var p2 = matrix[0, 1][0];
+        foreach (var fmt in Helper.NonComplexFormats)
+        {
+            var a = test.Cast(fmt);
+            var matrix = a.Measure(2, 1);
+            var p1 = matrix[0, 0][0];
+            var p2 = matrix[0, 1][0];
 
-                Assert.Equal(0, p1);
-                Assert.Equal(10, p2);
-            }
+            Assert.Equal(0, p1);
+            Assert.Equal(10, p2);
         }
+    }
 
-        [SkippableFact]
-        public void TestFindTrim()
+    [SkippableFact]
+    public void TestFindTrim()
+    {
+        Skip.IfNot(Helper.Have("find_trim"), "no find_trim in this vips, skipping test");
+
+        var im = Image.Black(50, 60) + 100;
+        var test = im.Embed(10, 20, 200, 300, extend: Enums.Extend.White);
+
+        foreach (var x in Helper.UnsignedFormats.Concat(Helper.FloatFormats).ToArray())
         {
-            Skip.IfNot(Helper.Have("find_trim"), "no find_trim in this vips, skipping test");
+            var a = test.Cast(x);
+            var trim = a.FindTrim();
+            var left = trim[0];
+            var top = trim[1];
+            var width = trim[2];
+            var height = trim[3];
 
-            var im = Image.Black(50, 60) + 100;
-            var test = im.Embed(10, 20, 200, 300, extend: Enums.Extend.White);
+            Assert.Equal(10, left);
+            Assert.Equal(20, top);
+            Assert.Equal(50, width);
+            Assert.Equal(60, height);
+        }
 
-            foreach (var x in Helper.UnsignedFormats.Concat(Helper.FloatFormats).ToArray())
-            {
-                var a = test.Cast(x);
-                var trim = a.FindTrim();
-                var left = trim[0];
-                var top = trim[1];
-                var width = trim[2];
-                var height = trim[3];
-
-                Assert.Equal(10, left);
-                Assert.Equal(20, top);
-                Assert.Equal(50, width);
-                Assert.Equal(60, height);
-            }
+        var testRgb = test.Bandjoin(test, test);
+        var trim2 = testRgb.FindTrim(background: new double[] { 255, 255, 255 });
+        var left2 = trim2[0];
+        var top2 = trim2[1];
+        var width2 = trim2[2];
+        var height2 = trim2[3];
 
-            var testRgb = test.Bandjoin(test, test);
-            var trim2 = testRgb.FindTrim(background: new double[] { 255, 255, 255 });
-            var left2 = trim2[0];
-            var top2 = trim2[1];
-            var width2 = trim2[2];
-            var height2 = trim2[3];
+        Assert.Equal(10, left2);
+        Assert.Equal(20, top2);
+        Assert.Equal(50, width2);
+        Assert.Equal(60, height2);
+    }
 
-            Assert.Equal(10, left2);
-            Assert.Equal(20, top2);
-            Assert.Equal(50, width2);
-            Assert.Equal(60, height2);
-        }
+    [Fact]
+    public void TestProfile()
+    {
+        var test = Image.Black(100, 100).Mutate(x => x.DrawRect(new double[] { 100 }, 40, 50, 1, 1));
 
-        [Fact]
-        public void TestProfile()
+        foreach (var fmt in Helper.NonComplexFormats)
         {
-            var test = Image.Black(100, 100).Mutate(x => x.DrawRect(new double[] { 100 }, 40, 50, 1, 1));
+            var profile = test.Cast(fmt).Profile();
+            var columns = (Image)profile[0];
+            var rows = (Image)profile[1];
 
-            foreach (var fmt in Helper.NonComplexFormats)
-            {
-                var profile = test.Cast(fmt).Profile();
-                var columns = (Image)profile[0];
-                var rows = (Image)profile[1];
-
-                var minPos = columns.MinPos();
-                var v = minPos[0];
-                var x = minPos[1];
-                var y = minPos[2];
-
-                Assert.Equal(50, v);
-                Assert.Equal(40, x);
-                Assert.Equal(0, y);
-
-                minPos = rows.MinPos();
-                v = minPos[0];
-                x = minPos[1];
-                y = minPos[2];
-
-                Assert.Equal(40, v);
-                Assert.Equal(0, x);
-                Assert.Equal(50, y);
-            }
+            var minPos = columns.MinPos();
+            var v = minPos[0];
+            var x = minPos[1];
+            var y = minPos[2];
+
+            Assert.Equal(50, v);
+            Assert.Equal(40, x);
+            Assert.Equal(0, y);
+
+            minPos = rows.MinPos();
+            v = minPos[0];
+            x = minPos[1];
+            y = minPos[2];
+
+            Assert.Equal(40, v);
+            Assert.Equal(0, x);
+            Assert.Equal(50, y);
         }
+    }
 
-        [Fact]
-        public void TestProject()
-        {
-            var im = Image.Black(50, 50);
-            var test = im.Insert(im + 10, 50, 0, expand: true);
+    [Fact]
+    public void TestProject()
+    {
+        var im = Image.Black(50, 50);
+        var test = im.Insert(im + 10, 50, 0, expand: true);
 
-            foreach (var fmt in Helper.NonComplexFormats)
-            {
-                var profile = test.Cast(fmt).Project();
-                var columns = (Image)profile[0];
-                var rows = (Image)profile[1];
+        foreach (var fmt in Helper.NonComplexFormats)
+        {
+            var profile = test.Cast(fmt).Project();
+            var columns = (Image)profile[0];
+            var rows = (Image)profile[1];
 
-                Assert.Equal(new double[] { 0 }, columns[10, 0]);
-                Assert.Equal(new double[] { 50 * 10 }, columns[70, 0]);
+            Assert.Equal(new double[] { 0 }, columns[10, 0]);
+            Assert.Equal(new double[] { 50 * 10 }, columns[70, 0]);
 
-                Assert.Equal(new double[] { 50 * 10 }, rows[0, 10]);
-            }
+            Assert.Equal(new double[] { 50 * 10 }, rows[0, 10]);
         }
+    }
+
+    [Fact]
+    public void TestStats()
+    {
+        var im = Image.Black(50, 50);
+        var test = im.Insert(im + 10, 50, 0, expand: true);
 
-        [Fact]
-        public void TestStats()
+        foreach (var fmt in Helper.NonComplexFormats)
         {
-            var im = Image.Black(50, 50);
-            var test = im.Insert(im + 10, 50, 0, expand: true);
+            var a = test.Cast(fmt);
+            var matrix = a.Stats();
 
-            foreach (var fmt in Helper.NonComplexFormats)
-            {
-                var a = test.Cast(fmt);
-                var matrix = a.Stats();
-
-                Assert.Equal(new[] { a.Min() }, matrix[0, 0]);
-                Assert.Equal(new[] { a.Max() }, matrix[1, 0]);
-                Assert.Equal(new double[] { 50 * 50 * 10 }, matrix[2, 0]);
-                Assert.Equal(new double[] { 50 * 50 * 100 }, matrix[3, 0]);
-                Assert.Equal(new[] { a.Avg() }, matrix[4, 0]);
-                Assert.Equal(new[] { a.Deviate() }, matrix[5, 0]);
-
-                Assert.Equal(new[] { a.Min() }, matrix[0, 1]);
-                Assert.Equal(new[] { a.Max() }, matrix[1, 1]);
-                Assert.Equal(new double[] { 50 * 50 * 10 }, matrix[2, 1]);
-                Assert.Equal(new double[] { 50 * 50 * 100 }, matrix[3, 1]);
-                Assert.Equal(new[] { a.Avg() }, matrix[4, 1]);
-                Assert.Equal(new[] { a.Deviate() }, matrix[5, 1]);
-            }
+            Assert.Equal(new[] { a.Min() }, matrix[0, 0]);
+            Assert.Equal(new[] { a.Max() }, matrix[1, 0]);
+            Assert.Equal(new double[] { 50 * 50 * 10 }, matrix[2, 0]);
+            Assert.Equal(new double[] { 50 * 50 * 100 }, matrix[3, 0]);
+            Assert.Equal(new[] { a.Avg() }, matrix[4, 0]);
+            Assert.Equal(new[] { a.Deviate() }, matrix[5, 0]);
+
+            Assert.Equal(new[] { a.Min() }, matrix[0, 1]);
+            Assert.Equal(new[] { a.Max() }, matrix[1, 1]);
+            Assert.Equal(new double[] { 50 * 50 * 10 }, matrix[2, 1]);
+            Assert.Equal(new double[] { 50 * 50 * 100 }, matrix[3, 1]);
+            Assert.Equal(new[] { a.Avg() }, matrix[4, 1]);
+            Assert.Equal(new[] { a.Deviate() }, matrix[5, 1]);
         }
+    }
 
-        [Fact]
-        public void TestSum()
+    [Fact]
+    public void TestSum()
+    {
+        foreach (var fmt in Helper.AllFormats)
         {
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var im = Image.Black(50, 50);
-                var im2 = Enumerable.Range(0, 100).Where(i => i % 10 == 0).Select(x => (im + x).Cast(fmt)).ToArray();
-                var im3 = Image.Sum(im2);
+            var im = Image.Black(50, 50);
+            var im2 = Enumerable.Range(0, 100).Where(i => i % 10 == 0).Select(x => (im + x).Cast(fmt)).ToArray();
+            var im3 = Image.Sum(im2);
 
-                Assert.Equal(Enumerable.Range(0, 100).Where(i => i % 10 == 0).Sum(), im3.Max());
-            }
+            Assert.Equal(Enumerable.Range(0, 100).Where(i => i % 10 == 0).Sum(), im3.Max());
         }
-
-        #endregion
     }
+
+    #endregion
 }
\ No newline at end of file
diff --git a/tests/NetVips.Tests/ColourTests.cs b/tests/NetVips.Tests/ColourTests.cs
index 5d21f424..042da781 100644
--- a/tests/NetVips.Tests/ColourTests.cs
+++ b/tests/NetVips.Tests/ColourTests.cs
@@ -1,237 +1,236 @@
-namespace NetVips.Tests
+using System;
+using System.Linq;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace NetVips.Tests;
+
+public class ColourTests : IClassFixture
 {
-    using System;
-    using System.Linq;
-    using Xunit;
-    using Xunit.Abstractions;
+    public ColourTests(TestsFixture testsFixture, ITestOutputHelper output)
+    {
+        testsFixture.SetUpLogging(output);
+    }
 
-    public class ColourTests : IClassFixture
+    [Fact]
+    public void TestColourspace()
     {
-        public ColourTests(TestsFixture testsFixture, ITestOutputHelper output)
+        // mid-grey in Lab ... put 42 in the extra band, it should be copied
+        // unmodified
+        var test = Image.Black(100, 100) + new[] { 50, 0, 0, 42 };
+        test = test.Copy(interpretation: Enums.Interpretation.Lab);
+
+        // a long series should come in a circle
+        var im = test;
+        foreach (var col in Helper.ColourColourspaces.Concat(new[] { Enums.Interpretation.Lab }))
         {
-            testsFixture.SetUpLogging(output);
-        }
+            im = im.Colourspace(col);
+            Assert.Equal(col, im.Interpretation);
 
-        [Fact]
-        public void TestColourspace()
-        {
-            // mid-grey in Lab ... put 42 in the extra band, it should be copied
-            // unmodified
-            var test = Image.Black(100, 100) + new[] { 50, 0, 0, 42 };
-            test = test.Copy(interpretation: Enums.Interpretation.Lab);
-
-            // a long series should come in a circle
-            var im = test;
-            foreach (var col in Helper.ColourColourspaces.Concat(new[] { Enums.Interpretation.Lab }))
+            for (var i = 0; i < 4; i++)
             {
-                im = im.Colourspace(col);
-                Assert.Equal(col, im.Interpretation);
-
-                for (var i = 0; i < 4; i++)
-                {
-                    var minL = im[i].Min();
-                    var maxH = im[i].Max();
-                    Assert.Equal(minL, maxH);
-                }
-
-                var pixel = im[10, 10];
-                if (col == Enums.Interpretation.Scrgb && NetVips.AtLeastLibvips(8, 15))
-                {
-                    // libvips 8.15 uses alpha range of 0.0 - 1.0 for scRGB images.
-                    Assert.Equal(42.0 / 255.0, pixel[3], 4);
-                }
-                else
-                {
-                    Assert.Equal(42, pixel[3], 2);
-                }
+                var minL = im[i].Min();
+                var maxH = im[i].Max();
+                Assert.Equal(minL, maxH);
             }
 
-            // alpha won't be equal for RGB16, but it should be preserved if we go
-            // there and back
-            im = im.Colourspace(Enums.Interpretation.Rgb16);
-            im = im.Colourspace(Enums.Interpretation.Lab);
-
-            var before = test[10, 10];
-            var after = im[10, 10];
-            Helper.AssertAlmostEqualObjects(before, after, 0.1);
-
-            // go between every pair of colour spaces
-            foreach (var start in Helper.ColourColourspaces)
+            var pixel = im[10, 10];
+            if (col == Enums.Interpretation.Scrgb && NetVips.AtLeastLibvips(8, 15))
             {
-                foreach (var end in Helper.ColourColourspaces)
-                {
-                    im = test.Colourspace(start);
-                    var im2 = im.Colourspace(end);
-                    var im3 = im2.Colourspace(Enums.Interpretation.Lab);
-                    before = test[10, 10];
-                    after = im3[10, 10];
-                    Helper.AssertAlmostEqualObjects(before, after, 0.1);
-                }
+                // libvips 8.15 uses alpha range of 0.0 - 1.0 for scRGB images.
+                Assert.Equal(42.0 / 255.0, pixel[3], 4);
             }
-
-            // test Lab->XYZ on mid-grey
-            // checked against http://www.brucelindbloom.com
-            im = test.Colourspace(Enums.Interpretation.Xyz);
-            after = im[10, 10];
-            Helper.AssertAlmostEqualObjects(new[]
+            else
             {
-                17.5064,
-                18.4187,
-                20.0547,
-                42
-            }, after);
-
-            // grey->colour->grey should be equal
-            foreach (var monoFmt in Helper.MonoColourspaces)
-            {
-                var testGrey = test.Colourspace(monoFmt);
-                im = testGrey;
-                foreach (var col in Helper.ColourColourspaces.Concat(new[] { monoFmt }))
-                {
-                    im = im.Colourspace(col);
-                    Assert.Equal(col, im.Interpretation);
-                }
-
-                var pixelBefore = testGrey[10, 10];
-                var alphaBefore = pixelBefore[1];
-                var pixelAfter = im[10, 10];
-                var alphaAfter = pixelAfter[1];
-                Assert.True(Math.Abs(alphaAfter - alphaBefore) < 1);
-
-                // GREY16 can wind up rather different due to rounding but 8-bit we should hit exactly
-                Assert.True(
-                    Math.Abs(pixelAfter[0] - pixelBefore[0]) < (monoFmt == Enums.Interpretation.Grey16 ? 30 : 1));
+                Assert.Equal(42, pixel[3], 2);
             }
+        }
+
+        // alpha won't be equal for RGB16, but it should be preserved if we go
+        // there and back
+        im = im.Colourspace(Enums.Interpretation.Rgb16);
+        im = im.Colourspace(Enums.Interpretation.Lab);
 
-            if (NetVips.AtLeastLibvips(8, 8))
+        var before = test[10, 10];
+        var after = im[10, 10];
+        Helper.AssertAlmostEqualObjects(before, after, 0.1);
+
+        // go between every pair of colour spaces
+        foreach (var start in Helper.ColourColourspaces)
+        {
+            foreach (var end in Helper.ColourColourspaces)
             {
-                // we should be able to go from cmyk to any 3-band space and back again,
-                // approximately
-                var cmyk = test.Colourspace(Enums.Interpretation.Cmyk);
-                foreach (var end in Helper.ColourColourspaces)
-                {
-                    im = cmyk.Colourspace(end);
-                    var im2 = im.Colourspace(Enums.Interpretation.Cmyk);
-
-                    before = cmyk[10, 10];
-                    after = im2[10, 10];
-
-                    Helper.AssertAlmostEqualObjects(before, after, 10);
-                }
+                im = test.Colourspace(start);
+                var im2 = im.Colourspace(end);
+                var im3 = im2.Colourspace(Enums.Interpretation.Lab);
+                before = test[10, 10];
+                after = im3[10, 10];
+                Helper.AssertAlmostEqualObjects(before, after, 0.1);
             }
         }
 
-        /// 
-        /// test results from Bruce Lindbloom's calculator:s
-        /// http://www.brucelindbloom.com
-        /// 
-        [Fact]
-        public void TestDE00()
+        // test Lab->XYZ on mid-grey
+        // checked against http://www.brucelindbloom.com
+        im = test.Colourspace(Enums.Interpretation.Xyz);
+        after = im[10, 10];
+        Helper.AssertAlmostEqualObjects(new[]
         {
-            // put 42 in the extra band, it should be copied unmodified
-            var reference = Image.Black(100, 100) + new[] { 50, 10, 20, 42 };
-            reference = reference.Copy(interpretation: Enums.Interpretation.Lab);
-            var sample = Image.Black(100, 100) + new[] { 40, -20, 10 };
-            sample = sample.Copy(interpretation: Enums.Interpretation.Lab);
-
-            var difference = reference.DE00(sample);
-            var diffPixel = difference[10, 10];
-            Assert.Equal(30.238, diffPixel[0], 3);
-            Assert.Equal(42.0, diffPixel[1], 3);
-        }
-
-        [Fact]
-        public void TestDE76()
+            17.5064,
+            18.4187,
+            20.0547,
+            42
+        }, after);
+
+        // grey->colour->grey should be equal
+        foreach (var monoFmt in Helper.MonoColourspaces)
         {
-            // put 42 in the extra band, it should be copied unmodified
-            var reference = Image.Black(100, 100) + new[] { 50, 10, 20, 42 };
-            reference = reference.Copy(interpretation: Enums.Interpretation.Lab);
-            var sample = Image.Black(100, 100) + new[] { 40, -20, 10 };
-            sample = sample.Copy(interpretation: Enums.Interpretation.Lab);
-
-            var difference = reference.DE76(sample);
-            var diffPixel = difference[10, 10];
-            Assert.Equal(33.166, diffPixel[0], 3);
-            Assert.Equal(42.0, diffPixel[1], 3);
+            var testGrey = test.Colourspace(monoFmt);
+            im = testGrey;
+            foreach (var col in Helper.ColourColourspaces.Concat(new[] { monoFmt }))
+            {
+                im = im.Colourspace(col);
+                Assert.Equal(col, im.Interpretation);
+            }
+
+            var pixelBefore = testGrey[10, 10];
+            var alphaBefore = pixelBefore[1];
+            var pixelAfter = im[10, 10];
+            var alphaAfter = pixelAfter[1];
+            Assert.True(Math.Abs(alphaAfter - alphaBefore) < 1);
+
+            // GREY16 can wind up rather different due to rounding but 8-bit we should hit exactly
+            Assert.True(
+                Math.Abs(pixelAfter[0] - pixelBefore[0]) < (monoFmt == Enums.Interpretation.Grey16 ? 30 : 1));
         }
 
-        /// 
-        /// the vips CMC calculation is based on distance in a colorspace
-        /// derived from the CMC formula, so it won't match exactly ...
-        /// see vips_LCh2CMC() for details
-        /// 
-        [Fact]
-        public void TestDECMC()
+        if (NetVips.AtLeastLibvips(8, 8))
         {
-            // put 42 in the extra band, it should be copied unmodified
-            var reference = Image.Black(100, 100) + new[] { 50, 10, 20, 42 };
-            reference = reference.Copy(interpretation: Enums.Interpretation.Lab);
-            var sample = Image.Black(100, 100) + new[] { 55, 11, 23 };
-            sample = sample.Copy(interpretation: Enums.Interpretation.Lab);
-
-            var difference = reference.DECMC(sample);
-            var diffPixel = difference[10, 10];
-            Assert.True(Math.Abs(diffPixel[0] - 4.97) < 0.5);
-            Assert.Equal(42.0, diffPixel[1], 3);
+            // we should be able to go from cmyk to any 3-band space and back again,
+            // approximately
+            var cmyk = test.Colourspace(Enums.Interpretation.Cmyk);
+            foreach (var end in Helper.ColourColourspaces)
+            {
+                im = cmyk.Colourspace(end);
+                var im2 = im.Colourspace(Enums.Interpretation.Cmyk);
+
+                before = cmyk[10, 10];
+                after = im2[10, 10];
+
+                Helper.AssertAlmostEqualObjects(before, after, 10);
+            }
         }
+    }
 
-        [SkippableFact]
-        public void TestIcc()
-        {
-            Skip.IfNot(Helper.Have("icc_import"), "no lcms support in this vips, skipping test");
+    /// 
+    /// test results from Bruce Lindbloom's calculator:s
+    /// http://www.brucelindbloom.com
+    /// 
+    [Fact]
+    public void TestDE00()
+    {
+        // put 42 in the extra band, it should be copied unmodified
+        var reference = Image.Black(100, 100) + new[] { 50, 10, 20, 42 };
+        reference = reference.Copy(interpretation: Enums.Interpretation.Lab);
+        var sample = Image.Black(100, 100) + new[] { 40, -20, 10 };
+        sample = sample.Copy(interpretation: Enums.Interpretation.Lab);
+
+        var difference = reference.DE00(sample);
+        var diffPixel = difference[10, 10];
+        Assert.Equal(30.238, diffPixel[0], 3);
+        Assert.Equal(42.0, diffPixel[1], 3);
+    }
 
-            var test = Image.NewFromFile(Helper.JpegFile);
+    [Fact]
+    public void TestDE76()
+    {
+        // put 42 in the extra band, it should be copied unmodified
+        var reference = Image.Black(100, 100) + new[] { 50, 10, 20, 42 };
+        reference = reference.Copy(interpretation: Enums.Interpretation.Lab);
+        var sample = Image.Black(100, 100) + new[] { 40, -20, 10 };
+        sample = sample.Copy(interpretation: Enums.Interpretation.Lab);
+
+        var difference = reference.DE76(sample);
+        var diffPixel = difference[10, 10];
+        Assert.Equal(33.166, diffPixel[0], 3);
+        Assert.Equal(42.0, diffPixel[1], 3);
+    }
 
-            var im = test.IccImport().IccExport();
-            Assert.True(im.DE76(test).Max() < 6);
+    /// 
+    /// the vips CMC calculation is based on distance in a colorspace
+    /// derived from the CMC formula, so it won't match exactly ...
+    /// see vips_LCh2CMC() for details
+    /// 
+    [Fact]
+    public void TestDECMC()
+    {
+        // put 42 in the extra band, it should be copied unmodified
+        var reference = Image.Black(100, 100) + new[] { 50, 10, 20, 42 };
+        reference = reference.Copy(interpretation: Enums.Interpretation.Lab);
+        var sample = Image.Black(100, 100) + new[] { 55, 11, 23 };
+        sample = sample.Copy(interpretation: Enums.Interpretation.Lab);
+
+        var difference = reference.DECMC(sample);
+        var diffPixel = difference[10, 10];
+        Assert.True(Math.Abs(diffPixel[0] - 4.97) < 0.5);
+        Assert.Equal(42.0, diffPixel[1], 3);
+    }
 
-            im = test.IccImport();
-            var im2 = im.IccExport(depth: 16);
-            Assert.Equal(Enums.BandFormat.Ushort, im2.Format);
-            var im3 = im2.IccImport();
-            Assert.True((im - im3).Abs().Max() < 3);
+    [SkippableFact]
+    public void TestIcc()
+    {
+        Skip.IfNot(Helper.Have("icc_import"), "no lcms support in this vips, skipping test");
 
-            im = test.IccImport(intent: Enums.Intent.Absolute);
+        var test = Image.NewFromFile(Helper.JpegFile);
 
-            im2 = im.IccExport(intent: Enums.Intent.Absolute);
-            Assert.True(im2.DE76(test).Max() < 6);
+        var im = test.IccImport().IccExport();
+        Assert.True(im.DE76(test).Max() < 6);
 
-            im = test.IccImport();
-            im2 = im.IccExport(outputProfile: Helper.SrgbFile);
-            im3 = im.Colourspace(Enums.Interpretation.Srgb);
-            Assert.True(im2.DE76(im3).Max() < 6);
+        im = test.IccImport();
+        var im2 = im.IccExport(depth: 16);
+        Assert.Equal(Enums.BandFormat.Ushort, im2.Format);
+        var im3 = im2.IccImport();
+        Assert.True((im - im3).Abs().Max() < 3);
 
-            var beforeProfile = (byte[])test.Get("icc-profile-data");
-            im = test.IccTransform(Helper.SrgbFile);
-            var afterProfile = (byte[])im.Get("icc-profile-data");
-            im2 = test.IccImport();
-            im3 = im2.Colourspace(Enums.Interpretation.Srgb);
-            Assert.True(im2.DE76(im3).Max() < 6);
-            Assert.NotEqual(beforeProfile.Length, afterProfile.Length);
+        im = test.IccImport(intent: Enums.Intent.Absolute);
 
-            im = test.IccImport(inputProfile: Helper.SrgbFile);
-            im2 = test.IccImport();
-            Assert.True(6 < im.DE76(im2).Max());
+        im2 = im.IccExport(intent: Enums.Intent.Absolute);
+        Assert.True(im2.DE76(test).Max() < 6);
 
-            im = test.IccImport(pcs: Enums.PCS.Xyz);
-            Assert.Equal(Enums.Interpretation.Xyz, im.Interpretation);
-            im = test.IccImport();
-            Assert.Equal(Enums.Interpretation.Lab, im.Interpretation);
-        }
+        im = test.IccImport();
+        im2 = im.IccExport(outputProfile: Helper.SrgbFile);
+        im3 = im.Colourspace(Enums.Interpretation.Srgb);
+        Assert.True(im2.DE76(im3).Max() < 6);
 
-        [SkippableFact]
-        public void TestCmyk()
-        {
-            Skip.IfNot(NetVips.AtLeastLibvips(8, 8), "requires libvips >= 8.8");
+        var beforeProfile = (byte[])test.Get("icc-profile-data");
+        im = test.IccTransform(Helper.SrgbFile);
+        var afterProfile = (byte[])im.Get("icc-profile-data");
+        im2 = test.IccImport();
+        im3 = im2.Colourspace(Enums.Interpretation.Srgb);
+        Assert.True(im2.DE76(im3).Max() < 6);
+        Assert.NotEqual(beforeProfile.Length, afterProfile.Length);
 
-            // even without lcms, we should have a working approximation
-            var test = Image.NewFromFile(Helper.JpegFile);
-            var im = test.Colourspace(Enums.Interpretation.Cmyk).Colourspace(Enums.Interpretation.Srgb);
+        im = test.IccImport(inputProfile: Helper.SrgbFile);
+        im2 = test.IccImport();
+        Assert.True(6 < im.DE76(im2).Max());
 
-            var before = test[582, 210];
-            var after = im[582, 210];
+        im = test.IccImport(pcs: Enums.PCS.Xyz);
+        Assert.Equal(Enums.Interpretation.Xyz, im.Interpretation);
+        im = test.IccImport();
+        Assert.Equal(Enums.Interpretation.Lab, im.Interpretation);
+    }
 
-            Helper.AssertAlmostEqualObjects(before, after, 10);
-        }
+    [SkippableFact]
+    public void TestCmyk()
+    {
+        Skip.IfNot(NetVips.AtLeastLibvips(8, 8), "requires libvips >= 8.8");
+
+        // even without lcms, we should have a working approximation
+        var test = Image.NewFromFile(Helper.JpegFile);
+        var im = test.Colourspace(Enums.Interpretation.Cmyk).Colourspace(Enums.Interpretation.Srgb);
+
+        var before = test[582, 210];
+        var after = im[582, 210];
+
+        Helper.AssertAlmostEqualObjects(before, after, 10);
     }
 }
\ No newline at end of file
diff --git a/tests/NetVips.Tests/ConnectionTests.cs b/tests/NetVips.Tests/ConnectionTests.cs
index d87a461a..800329a7 100644
--- a/tests/NetVips.Tests/ConnectionTests.cs
+++ b/tests/NetVips.Tests/ConnectionTests.cs
@@ -1,163 +1,162 @@
-namespace NetVips.Tests
+using System;
+using System.IO;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace NetVips.Tests;
+
+public class ConnectionTests : IClassFixture, IDisposable
 {
-    using System;
-    using System.IO;
-    using Xunit;
-    using Xunit.Abstractions;
+    private readonly string _tempDir;
 
-    public class ConnectionTests : IClassFixture, IDisposable
+    public ConnectionTests(TestsFixture testsFixture, ITestOutputHelper output)
     {
-        private readonly string _tempDir;
+        testsFixture.SetUpLogging(output);
 
-        public ConnectionTests(TestsFixture testsFixture, ITestOutputHelper output)
-        {
-            testsFixture.SetUpLogging(output);
+        _tempDir = Helper.GetTemporaryDirectory();
+    }
 
-            _tempDir = Helper.GetTemporaryDirectory();
+    public void Dispose()
+    {
+        try
+        {
+            Directory.Delete(_tempDir, true);
         }
-
-        public void Dispose()
+        catch (Exception)
         {
-            try
-            {
-                Directory.Delete(_tempDir, true);
-            }
-            catch (Exception)
-            {
-                // ignore
-            }
+            // ignore
         }
+    }
 
-        [SkippableFact]
-        public void TestConnection()
-        {
-            Skip.IfNot(Helper.Have("jpegload_source"), "no jpeg source support, skipping test");
+    [SkippableFact]
+    public void TestConnection()
+    {
+        Skip.IfNot(Helper.Have("jpegload_source"), "no jpeg source support, skipping test");
 
-            var source = Source.NewFromFile(Helper.JpegFile);
-            var image = Image.NewFromSource(source, access: Enums.Access.Sequential);
-            var filename = Helper.GetTemporaryFile(_tempDir, ".png");
-            var target = Target.NewToFile(filename);
+        var source = Source.NewFromFile(Helper.JpegFile);
+        var image = Image.NewFromSource(source, access: Enums.Access.Sequential);
+        var filename = Helper.GetTemporaryFile(_tempDir, ".png");
+        var target = Target.NewToFile(filename);
 
-            Assert.Equal(Helper.JpegFile, source.GetFileName());
-            Assert.Equal(filename, target.GetFileName());
+        Assert.Equal(Helper.JpegFile, source.GetFileName());
+        Assert.Equal(filename, target.GetFileName());
 
-            image.WriteToTarget(target, ".png");
+        image.WriteToTarget(target, ".png");
 
-            image = Image.NewFromFile(Helper.JpegFile, access: Enums.Access.Sequential);
-            var image2 = Image.NewFromFile(filename, access: Enums.Access.Sequential);
+        image = Image.NewFromFile(Helper.JpegFile, access: Enums.Access.Sequential);
+        var image2 = Image.NewFromFile(filename, access: Enums.Access.Sequential);
 
-            Assert.True((image - image2).Abs().Max() < 10);
-        }
+        Assert.True((image - image2).Abs().Max() < 10);
+    }
 
-        [SkippableFact]
-        public void TestSourceCustomNoSeek()
-        {
-            Skip.IfNot(Helper.Have("jpegload_source"), "no jpeg source support, skipping test");
+    [SkippableFact]
+    public void TestSourceCustomNoSeek()
+    {
+        Skip.IfNot(Helper.Have("jpegload_source"), "no jpeg source support, skipping test");
 
-            var input = File.OpenRead(Helper.JpegFile);
+        var input = File.OpenRead(Helper.JpegFile);
 
-            var source = new SourceCustom();
-            source.OnRead += (buffer, length) => input.Read(buffer, 0, length);
+        var source = new SourceCustom();
+        source.OnRead += (buffer, length) => input.Read(buffer, 0, length);
 
-            Assert.Null(source.GetFileName());
-            Assert.Equal("source_custom", source.GetNick());
+        Assert.Null(source.GetFileName());
+        Assert.Equal("source_custom", source.GetNick());
 
-            var image = Image.NewFromSource(source, access: Enums.Access.Sequential);
-            var image2 = Image.NewFromFile(Helper.JpegFile, access: Enums.Access.Sequential);
+        var image = Image.NewFromSource(source, access: Enums.Access.Sequential);
+        var image2 = Image.NewFromFile(Helper.JpegFile, access: Enums.Access.Sequential);
 
-            Assert.True((image - image2).Abs().Max() < 10);
-        }
+        Assert.True((image - image2).Abs().Max() < 10);
+    }
 
-        [SkippableFact]
-        public void TestSourceCustom()
-        {
-            Skip.IfNot(Helper.Have("jpegload_source"), "no jpeg source support, skipping test");
+    [SkippableFact]
+    public void TestSourceCustom()
+    {
+        Skip.IfNot(Helper.Have("jpegload_source"), "no jpeg source support, skipping test");
 
-            var input = File.OpenRead(Helper.JpegFile);
+        var input = File.OpenRead(Helper.JpegFile);
 
-            var source = new SourceCustom();
-            source.OnRead += (buffer, length) => input.Read(buffer, 0, length);
-            source.OnSeek += (offset, origin) => input.Seek(offset, origin);
+        var source = new SourceCustom();
+        source.OnRead += (buffer, length) => input.Read(buffer, 0, length);
+        source.OnSeek += (offset, origin) => input.Seek(offset, origin);
 
-            Assert.Null(source.GetFileName());
-            Assert.Equal("source_custom", source.GetNick());
+        Assert.Null(source.GetFileName());
+        Assert.Equal("source_custom", source.GetNick());
 
-            var image = Image.NewFromSource(source, access: Enums.Access.Sequential);
-            var image2 = Image.NewFromFile(Helper.JpegFile, access: Enums.Access.Sequential);
+        var image = Image.NewFromSource(source, access: Enums.Access.Sequential);
+        var image2 = Image.NewFromFile(Helper.JpegFile, access: Enums.Access.Sequential);
 
-            Assert.True((image - image2).Abs().Max() < 10);
-        }
+        Assert.True((image - image2).Abs().Max() < 10);
+    }
 
-        [SkippableFact]
-        public void TestTargetCustom()
-        {
-            Skip.IfNot(Helper.Have("jpegsave_target"), "no jpeg target support, skipping test");
+    [SkippableFact]
+    public void TestTargetCustom()
+    {
+        Skip.IfNot(Helper.Have("jpegsave_target"), "no jpeg target support, skipping test");
 
-            var filename = Helper.GetTemporaryFile(_tempDir, ".png");
-            var output = File.OpenWrite(filename);
+        var filename = Helper.GetTemporaryFile(_tempDir, ".png");
+        var output = File.OpenWrite(filename);
 
-            var target = new TargetCustom();
-            target.OnWrite += (buffer, length) =>
-            {
-                output.Write(buffer, 0, length);
-                return length;
-            };
-            target.OnEnd += () =>
-            {
-                output.Close();
-                return 0;
-            };
+        var target = new TargetCustom();
+        target.OnWrite += (buffer, length) =>
+        {
+            output.Write(buffer, 0, length);
+            return length;
+        };
+        target.OnEnd += () =>
+        {
+            output.Close();
+            return 0;
+        };
 
-            Assert.Null(target.GetFileName());
-            Assert.Equal("target_custom", target.GetNick());
+        Assert.Null(target.GetFileName());
+        Assert.Equal("target_custom", target.GetNick());
 
-            var image = Image.NewFromFile(Helper.JpegFile, access: Enums.Access.Sequential);
-            image.WriteToTarget(target, ".png");
+        var image = Image.NewFromFile(Helper.JpegFile, access: Enums.Access.Sequential);
+        image.WriteToTarget(target, ".png");
 
-            image = Image.NewFromFile(Helper.JpegFile, access: Enums.Access.Sequential);
-            var image2 = Image.NewFromFile(filename, access: Enums.Access.Sequential);
+        image = Image.NewFromFile(Helper.JpegFile, access: Enums.Access.Sequential);
+        var image2 = Image.NewFromFile(filename, access: Enums.Access.Sequential);
 
-            Assert.True((image - image2).Abs().Max() < 10);
-        }
+        Assert.True((image - image2).Abs().Max() < 10);
+    }
 
 
-        [SkippableFact]
-        public void TestSourceCustomWebpNoSeek()
-        {
-            Skip.IfNot(Helper.Have("webpload_source"), "no webp source support, skipping test");
+    [SkippableFact]
+    public void TestSourceCustomWebpNoSeek()
+    {
+        Skip.IfNot(Helper.Have("webpload_source"), "no webp source support, skipping test");
 
-            var input = File.OpenRead(Helper.WebpFile);
+        var input = File.OpenRead(Helper.WebpFile);
 
-            var source = new SourceCustom();
-            source.OnRead += (buffer, length) => input.Read(buffer, 0, length);
+        var source = new SourceCustom();
+        source.OnRead += (buffer, length) => input.Read(buffer, 0, length);
 
-            Assert.Null(source.GetFileName());
-            Assert.Equal("source_custom", source.GetNick());
+        Assert.Null(source.GetFileName());
+        Assert.Equal("source_custom", source.GetNick());
 
-            var image = Image.NewFromSource(source, access: Enums.Access.Sequential);
-            var image2 = Image.NewFromFile(Helper.WebpFile, access: Enums.Access.Sequential);
+        var image = Image.NewFromSource(source, access: Enums.Access.Sequential);
+        var image2 = Image.NewFromFile(Helper.WebpFile, access: Enums.Access.Sequential);
 
-            Assert.True((image - image2).Abs().Max() < 10);
-        }
+        Assert.True((image - image2).Abs().Max() < 10);
+    }
 
-        [SkippableFact]
-        public void TestSourceCustomWebp()
-        {
-            Skip.IfNot(Helper.Have("webpload_source"), "no webp source support, skipping test");
+    [SkippableFact]
+    public void TestSourceCustomWebp()
+    {
+        Skip.IfNot(Helper.Have("webpload_source"), "no webp source support, skipping test");
 
-            var input = File.OpenRead(Helper.WebpFile);
+        var input = File.OpenRead(Helper.WebpFile);
 
-            var source = new SourceCustom();
-            source.OnRead += (buffer, length) => input.Read(buffer, 0, length);
-            source.OnSeek += (offset, origin) => input.Seek(offset, origin);
+        var source = new SourceCustom();
+        source.OnRead += (buffer, length) => input.Read(buffer, 0, length);
+        source.OnSeek += (offset, origin) => input.Seek(offset, origin);
 
-            Assert.Null(source.GetFileName());
-            Assert.Equal("source_custom", source.GetNick());
+        Assert.Null(source.GetFileName());
+        Assert.Equal("source_custom", source.GetNick());
 
-            var image = Image.NewFromSource(source, access: Enums.Access.Sequential);
-            var image2 = Image.NewFromFile(Helper.WebpFile, access: Enums.Access.Sequential);
+        var image = Image.NewFromSource(source, access: Enums.Access.Sequential);
+        var image2 = Image.NewFromFile(Helper.WebpFile, access: Enums.Access.Sequential);
 
-            Assert.True((image - image2).Abs().Max() < 10);
-        }
+        Assert.True((image - image2).Abs().Max() < 10);
     }
 }
\ No newline at end of file
diff --git a/tests/NetVips.Tests/ConversionTests.cs b/tests/NetVips.Tests/ConversionTests.cs
index 2f236ef6..d009adc4 100644
--- a/tests/NetVips.Tests/ConversionTests.cs
+++ b/tests/NetVips.Tests/ConversionTests.cs
@@ -1,1135 +1,1134 @@
-namespace NetVips.Tests
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace NetVips.Tests;
+
+public class ConversionTests : IClassFixture
 {
-    using System;
-    using System.Collections.Generic;
-    using System.Linq;
-    using Xunit;
-    using Xunit.Abstractions;
+    private Image _image;
+    private Image _colour;
+    private Image _mono;
+    private Image[] _allImages;
 
-    public class ConversionTests : IClassFixture
+    public ConversionTests(TestsFixture testsFixture, ITestOutputHelper output)
     {
-        private Image _image;
-        private Image _colour;
-        private Image _mono;
-        private Image[] _allImages;
-
-        public ConversionTests(TestsFixture testsFixture, ITestOutputHelper output)
+        testsFixture.SetUpLogging(output);
+
+        var im = Image.MaskIdeal(100, 100, 0.5, reject: true, optical: true);
+        _colour = im * new[] { 1, 2, 3 } + new[] { 2, 3, 4 };
+        _colour = _colour.Copy(interpretation: Enums.Interpretation.Srgb);
+        _mono = _colour[1];
+        _mono = _mono.Copy(interpretation: Enums.Interpretation.Bw);
+        _allImages = new[]
         {
-            testsFixture.SetUpLogging(output);
-
-            var im = Image.MaskIdeal(100, 100, 0.5, reject: true, optical: true);
-            _colour = im * new[] { 1, 2, 3 } + new[] { 2, 3, 4 };
-            _colour = _colour.Copy(interpretation: Enums.Interpretation.Srgb);
-            _mono = _colour[1];
-            _mono = _mono.Copy(interpretation: Enums.Interpretation.Bw);
-            _allImages = new[]
-            {
-                _mono,
-                _colour
-            };
-            _image = Image.Jpegload(Helper.JpegFile);
-        }
+            _mono,
+            _colour
+        };
+        _image = Image.Jpegload(Helper.JpegFile);
+    }
 
-        #region helpers
+    #region helpers
 
-        /// 
-        /// run a function on an image,
-        /// 50,50 and 10,10 should have different values on the test image
-        /// don't loop over band elements
-        /// 
-        /// 
-        /// 
-        internal void RunImagePixels(Image im, Func func)
-        {
-            Helper.RunCmp(im, 50, 50, func);
-            Helper.RunCmp(im, 10, 10, func);
-        }
+    /// 
+    /// run a function on an image,
+    /// 50,50 and 10,10 should have different values on the test image
+    /// don't loop over band elements
+    /// 
+    /// 
+    /// 
+    internal void RunImagePixels(Image im, Func func)
+    {
+        Helper.RunCmp(im, 50, 50, func);
+        Helper.RunCmp(im, 10, 10, func);
+    }
 
-        /// 
-        /// run a function on a pair of images
-        /// 50,50 and 10,10 should have different values on the test image
-        /// don't loop over band elements
-        /// 
-        /// 
-        /// 
-        /// 
-        internal void RunImagePixels2(Image left, Image right, Func func)
-        {
-            Helper.RunCmp2(left, right, 50, 50, func);
-            Helper.RunCmp2(left, right, 10, 10, func);
-        }
+    /// 
+    /// run a function on a pair of images
+    /// 50,50 and 10,10 should have different values on the test image
+    /// don't loop over band elements
+    /// 
+    /// 
+    /// 
+    /// 
+    internal void RunImagePixels2(Image left, Image right, Func func)
+    {
+        Helper.RunCmp2(left, right, 50, 50, func);
+        Helper.RunCmp2(left, right, 10, 10, func);
+    }
 
-        internal void RunUnary(IEnumerable images, Func func,
-            Enums.BandFormat[] formats = null)
-        {
-            formats ??= Helper.AllFormats;
+    internal void RunUnary(IEnumerable images, Func func,
+        Enums.BandFormat[] formats = null)
+    {
+        formats ??= Helper.AllFormats;
 
-            foreach (var x in images)
+        foreach (var x in images)
+        {
+            foreach (var y in formats)
             {
-                foreach (var y in formats)
-                {
-                    RunImagePixels(x.Cast(y), func);
-                }
+                RunImagePixels(x.Cast(y), func);
             }
         }
+    }
 
-        internal void RunBinary(IEnumerable images, Func func,
-            Enums.BandFormat[] formats = null)
-        {
-            formats ??= Helper.AllFormats;
+    internal void RunBinary(IEnumerable images, Func func,
+        Enums.BandFormat[] formats = null)
+    {
+        formats ??= Helper.AllFormats;
 
-            foreach (var x in images)
+        foreach (var x in images)
+        {
+            foreach (var y in formats)
             {
-                foreach (var y in formats)
+                foreach (var z in formats)
                 {
-                    foreach (var z in formats)
-                    {
-                        RunImagePixels2(x.Cast(y), x.Cast(z), func);
-                    }
+                    RunImagePixels2(x.Cast(y), x.Cast(z), func);
                 }
             }
         }
+    }
 
-        #endregion
+    #endregion
 
-        [Fact]
-        public void TestBandAnd()
+    [Fact]
+    public void TestBandAnd()
+    {
+        dynamic BandAnd(dynamic x)
         {
-            dynamic BandAnd(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.BandAnd();
-                }
-
-                return ((IEnumerable)x).Aggregate((a, b) => (int)a & (int)b);
+                return image.BandAnd();
             }
 
-            RunUnary(_allImages, BandAnd, Helper.IntFormats);
+            return ((IEnumerable)x).Aggregate((a, b) => (int)a & (int)b);
         }
 
-        [Fact]
-        public void TestBandOr()
+        RunUnary(_allImages, BandAnd, Helper.IntFormats);
+    }
+
+    [Fact]
+    public void TestBandOr()
+    {
+        dynamic BandOr(dynamic x)
         {
-            dynamic BandOr(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.BandOr();
-                }
-
-                return ((IEnumerable)x).Aggregate((a, b) => (int)a | (int)b);
+                return image.BandOr();
             }
 
-            RunUnary(_allImages, BandOr, Helper.IntFormats);
+            return ((IEnumerable)x).Aggregate((a, b) => (int)a | (int)b);
         }
 
-        [Fact]
-        public void TestBandEor()
+        RunUnary(_allImages, BandOr, Helper.IntFormats);
+    }
+
+    [Fact]
+    public void TestBandEor()
+    {
+        dynamic BandEor(dynamic x)
         {
-            dynamic BandEor(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.BandEor();
-                }
-
-                return ((IEnumerable)x).Aggregate((a, b) => (int)a ^ (int)b);
+                return image.BandEor();
             }
 
-            RunUnary(_allImages, BandEor, Helper.IntFormats);
+            return ((IEnumerable)x).Aggregate((a, b) => (int)a ^ (int)b);
         }
 
-        [Fact]
-        public void TestBandJoin()
+        RunUnary(_allImages, BandEor, Helper.IntFormats);
+    }
+
+    [Fact]
+    public void TestBandJoin()
+    {
+        dynamic BandJoin(dynamic x, dynamic y)
         {
-            dynamic BandJoin(dynamic x, dynamic y)
+            if (x is Image left)
             {
-                if (x is Image left)
-                {
-                    return left.Bandjoin(y);
-                }
-
-                return ((IEnumerable)x).Concat((IEnumerable)y);
+                return left.Bandjoin(y);
             }
 
-            RunBinary(_allImages, BandJoin);
+            return ((IEnumerable)x).Concat((IEnumerable)y);
         }
 
-        [Fact]
-        public void TestBandJoinConst()
-        {
-            var x = _colour.Bandjoin(1);
-            Assert.Equal(4, x.Bands);
-            Assert.Equal(1, x[3].Avg());
-
-            x = _colour.Bandjoin(1, 2);
-            Assert.Equal(5, x.Bands);
-            Assert.Equal(1, x[3].Avg());
-            Assert.Equal(2, x[4].Avg());
-        }
+        RunBinary(_allImages, BandJoin);
+    }
 
-        [Fact]
-        public void TestAddAlpha()
-        {
-            var x = _colour.AddAlpha();
-            Assert.Equal(4, x.Bands);
-            Assert.Equal(255, x[3].Avg());
+    [Fact]
+    public void TestBandJoinConst()
+    {
+        var x = _colour.Bandjoin(1);
+        Assert.Equal(4, x.Bands);
+        Assert.Equal(1, x[3].Avg());
+
+        x = _colour.Bandjoin(1, 2);
+        Assert.Equal(5, x.Bands);
+        Assert.Equal(1, x[3].Avg());
+        Assert.Equal(2, x[4].Avg());
+    }
 
-            x = _colour.Copy(interpretation: Enums.Interpretation.Rgb16).AddAlpha();
-            Assert.Equal(4, x.Bands);
-            Assert.Equal(65535, x[3].Avg());
-        }
+    [Fact]
+    public void TestAddAlpha()
+    {
+        var x = _colour.AddAlpha();
+        Assert.Equal(4, x.Bands);
+        Assert.Equal(255, x[3].Avg());
+
+        x = _colour.Copy(interpretation: Enums.Interpretation.Rgb16).AddAlpha();
+        Assert.Equal(4, x.Bands);
+        Assert.Equal(65535, x[3].Avg());
+    }
 
-        [Fact]
-        public void TestBandMean()
+    [Fact]
+    public void TestBandMean()
+    {
+        dynamic BandMean(dynamic x)
         {
-            dynamic BandMean(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.Bandmean();
-                }
-
-                return new[] { Math.Floor(((IEnumerable)x).Sum() / x.Length) };
+                return image.Bandmean();
             }
 
-            RunUnary(_allImages, BandMean, Helper.NonComplexFormats);
+            return new[] { Math.Floor(((IEnumerable)x).Sum() / x.Length) };
         }
 
-        [Fact]
-        public void TestBandRank()
+        RunUnary(_allImages, BandMean, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestBandRank()
+    {
+        double[] Median(IEnumerable x, IEnumerable y)
         {
-            double[] Median(IEnumerable x, IEnumerable y)
-            {
-                var joined = x.Zip(y, (d, d1) => new[] { d, d1 }).OrderBy(o => o[0]);
+            var joined = x.Zip(y, (d, d1) => new[] { d, d1 }).OrderBy(o => o[0]);
 
-                return joined.Select(z => z[z.Length / 2]).ToArray();
-            }
+            return joined.Select(z => z[z.Length / 2]).ToArray();
+        }
 
-            dynamic BandRank(dynamic x, dynamic y)
+        dynamic BandRank(dynamic x, dynamic y)
+        {
+            if (x is Image left)
             {
-                if (x is Image left)
-                {
-                    return left.Bandrank(y);
-                }
-
-                return Median(x, y);
+                return left.Bandrank(y);
             }
 
-            RunBinary(_allImages, BandRank, Helper.NonComplexFormats);
-
-            // we can mix images and constants, and set the index arg
-            var a = _mono.Bandrank(new[] { 2 }, index: 0);
-            var b = (_mono < 2).Ifthenelse(_mono, 2);
-            Assert.Equal(0, (a - b).Abs().Min());
+            return Median(x, y);
         }
 
-        [Fact]
-        public void TestCache()
+        RunBinary(_allImages, BandRank, Helper.NonComplexFormats);
+
+        // we can mix images and constants, and set the index arg
+        var a = _mono.Bandrank(new[] { 2 }, index: 0);
+        var b = (_mono < 2).Ifthenelse(_mono, 2);
+        Assert.Equal(0, (a - b).Abs().Min());
+    }
+
+    [Fact]
+    public void TestCache()
+    {
+        dynamic Cache(dynamic x)
         {
-            dynamic Cache(dynamic x)
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.Cache();
-                }
-
-                return x;
+                return image.Cache();
             }
 
-            RunUnary(_allImages, Cache);
+            return x;
         }
 
-        [Fact]
-        public void TestCopy()
-        {
-            var x = _colour.Copy(interpretation: Enums.Interpretation.Lab);
-            Assert.Equal(Enums.Interpretation.Lab, x.Interpretation);
-            x = _colour.Copy(xres: 42);
-            Assert.Equal(42, x.Xres);
-            x = _colour.Copy(yres: 42);
-            Assert.Equal(42, x.Yres);
-            x = _colour.Copy(xoffset: 42);
-            Assert.Equal(42, x.Xoffset);
-            x = _colour.Copy(yoffset: 42);
-            Assert.Equal(42, x.Yoffset);
-            x = _colour.Copy(coding: Enums.Coding.None);
-            Assert.Equal(Enums.Coding.None, x.Coding);
-        }
+        RunUnary(_allImages, Cache);
+    }
 
-        [Fact]
-        public void TestBandfold()
-        {
-            var x = _mono.Bandfold();
-            Assert.Equal(1, x.Width);
-            Assert.Equal(_mono.Width, x.Bands);
-
-            var y = x.Bandunfold();
-            Assert.Equal(_mono.Width, y.Width);
-            Assert.Equal(1, y.Bands);
-            Assert.Equal(x.Avg(), y.Avg());
-
-            x = _mono.Bandfold(factor: 2);
-            Assert.Equal(_mono.Width / 2, x.Width);
-            Assert.Equal(2, x.Bands);
-
-            y = x.Bandunfold(factor: 2);
-            Assert.Equal(_mono.Width, y.Width);
-            Assert.Equal(1, y.Bands);
-            Assert.Equal(x.Avg(), y.Avg());
-        }
+    [Fact]
+    public void TestCopy()
+    {
+        var x = _colour.Copy(interpretation: Enums.Interpretation.Lab);
+        Assert.Equal(Enums.Interpretation.Lab, x.Interpretation);
+        x = _colour.Copy(xres: 42);
+        Assert.Equal(42, x.Xres);
+        x = _colour.Copy(yres: 42);
+        Assert.Equal(42, x.Yres);
+        x = _colour.Copy(xoffset: 42);
+        Assert.Equal(42, x.Xoffset);
+        x = _colour.Copy(yoffset: 42);
+        Assert.Equal(42, x.Yoffset);
+        x = _colour.Copy(coding: Enums.Coding.None);
+        Assert.Equal(Enums.Coding.None, x.Coding);
+    }
 
-        [Fact]
-        public void TestByteswap()
-        {
-            var x = _mono.Cast(Enums.BandFormat.Ushort);
-            var y = x.Byteswap().Byteswap();
-            Assert.Equal(x.Width, y.Width);
-            Assert.Equal(x.Height, y.Height);
-            Assert.Equal(x.Bands, y.Bands);
-            Assert.Equal(x.Avg(), y.Avg());
-        }
+    [Fact]
+    public void TestBandfold()
+    {
+        var x = _mono.Bandfold();
+        Assert.Equal(1, x.Width);
+        Assert.Equal(_mono.Width, x.Bands);
+
+        var y = x.Bandunfold();
+        Assert.Equal(_mono.Width, y.Width);
+        Assert.Equal(1, y.Bands);
+        Assert.Equal(x.Avg(), y.Avg());
+
+        x = _mono.Bandfold(factor: 2);
+        Assert.Equal(_mono.Width / 2, x.Width);
+        Assert.Equal(2, x.Bands);
+
+        y = x.Bandunfold(factor: 2);
+        Assert.Equal(_mono.Width, y.Width);
+        Assert.Equal(1, y.Bands);
+        Assert.Equal(x.Avg(), y.Avg());
+    }
 
-        [Fact]
-        public void TestEmbed()
-        {
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var test = _colour.Cast(fmt);
-
-                var im = test.Embed(20, 20, _colour.Width + 40, _colour.Height + 40);
-                var pixel = im[10, 10];
-                Assert.Equal(new double[] { 0, 0, 0 }, pixel);
-                pixel = im[30, 30];
-                Assert.Equal(new double[] { 2, 3, 4 }, pixel);
-                pixel = im[im.Width - 10, im.Height - 10];
-                Assert.Equal(new double[] { 0, 0, 0 }, pixel);
-
-                im = test.Embed(20, 20, _colour.Width + 40, _colour.Height + 40, extend: Enums.Extend.Copy);
-                pixel = im[10, 10];
-                Assert.Equal(new double[] { 2, 3, 4 }, pixel);
-                pixel = im[im.Width - 10, im.Height - 10];
-                Assert.Equal(new double[] { 2, 3, 4 }, pixel);
-
-                im = test.Embed(20, 20, _colour.Width + 40, _colour.Height + 40, extend: Enums.Extend.Background,
-                    background: new double[] { 7, 8, 9 });
-                pixel = im[10, 10];
-                Assert.Equal(new double[] { 7, 8, 9 }, pixel);
-                pixel = im[im.Width - 10, im.Height - 10];
-                Assert.Equal(new double[] { 7, 8, 9 }, pixel);
-
-                im = test.Embed(20, 20, _colour.Width + 40, _colour.Height + 40, extend: Enums.Extend.White);
-
-                pixel = im[10, 10];
-
-                // uses 255 in all bytes of ints, 255.0 for float
-                var pixelLongs = pixel.Select(x => (double)(Convert.ToInt64(x) & 255));
-                Assert.Equal(new double[] { 255, 255, 255 }, pixelLongs);
-                pixel = im[im.Width - 10, im.Height - 10];
-                pixelLongs = pixel.Select(x => (double)(Convert.ToInt64(x) & 255));
-                Assert.Equal(new double[] { 255, 255, 255 }, pixelLongs);
-            }
-        }
+    [Fact]
+    public void TestByteswap()
+    {
+        var x = _mono.Cast(Enums.BandFormat.Ushort);
+        var y = x.Byteswap().Byteswap();
+        Assert.Equal(x.Width, y.Width);
+        Assert.Equal(x.Height, y.Height);
+        Assert.Equal(x.Bands, y.Bands);
+        Assert.Equal(x.Avg(), y.Avg());
+    }
 
-        [SkippableFact]
-        public void TestGravity()
+    [Fact]
+    public void TestEmbed()
+    {
+        foreach (var fmt in Helper.AllFormats)
         {
-            Skip.IfNot(Helper.Have("gravity"), "no gravity in this vips, skipping test");
+            var test = _colour.Cast(fmt);
+
+            var im = test.Embed(20, 20, _colour.Width + 40, _colour.Height + 40);
+            var pixel = im[10, 10];
+            Assert.Equal(new double[] { 0, 0, 0 }, pixel);
+            pixel = im[30, 30];
+            Assert.Equal(new double[] { 2, 3, 4 }, pixel);
+            pixel = im[im.Width - 10, im.Height - 10];
+            Assert.Equal(new double[] { 0, 0, 0 }, pixel);
+
+            im = test.Embed(20, 20, _colour.Width + 40, _colour.Height + 40, extend: Enums.Extend.Copy);
+            pixel = im[10, 10];
+            Assert.Equal(new double[] { 2, 3, 4 }, pixel);
+            pixel = im[im.Width - 10, im.Height - 10];
+            Assert.Equal(new double[] { 2, 3, 4 }, pixel);
+
+            im = test.Embed(20, 20, _colour.Width + 40, _colour.Height + 40, extend: Enums.Extend.Background,
+                background: new double[] { 7, 8, 9 });
+            pixel = im[10, 10];
+            Assert.Equal(new double[] { 7, 8, 9 }, pixel);
+            pixel = im[im.Width - 10, im.Height - 10];
+            Assert.Equal(new double[] { 7, 8, 9 }, pixel);
+
+            im = test.Embed(20, 20, _colour.Width + 40, _colour.Height + 40, extend: Enums.Extend.White);
+
+            pixel = im[10, 10];
+
+            // uses 255 in all bytes of ints, 255.0 for float
+            var pixelLongs = pixel.Select(x => (double)(Convert.ToInt64(x) & 255));
+            Assert.Equal(new double[] { 255, 255, 255 }, pixelLongs);
+            pixel = im[im.Width - 10, im.Height - 10];
+            pixelLongs = pixel.Select(x => (double)(Convert.ToInt64(x) & 255));
+            Assert.Equal(new double[] { 255, 255, 255 }, pixelLongs);
+        }
+    }
 
-            var im = Image.Black(1, 1) + 255;
-            var positions = new Dictionary
-            {
-                {Enums.CompassDirection.Centre, new[] {1, 1}},
-                {Enums.CompassDirection.North, new[] {1, 0}},
-                {Enums.CompassDirection.South, new[] {1, 2}},
-                {Enums.CompassDirection.East, new[] {2, 1}},
-                {Enums.CompassDirection.West, new[] {0, 1}},
-                {Enums.CompassDirection.NorthEast, new[] {2, 0}},
-                {Enums.CompassDirection.SouthEast, new[] {2, 2}},
-                {Enums.CompassDirection.SouthWest, new[] {0, 2}},
-                {Enums.CompassDirection.NorthWest, new[] {0, 0}}
-            };
+    [SkippableFact]
+    public void TestGravity()
+    {
+        Skip.IfNot(Helper.Have("gravity"), "no gravity in this vips, skipping test");
 
-            foreach (var kvp in positions)
-            {
-                var direction = kvp.Key;
-                var x = kvp.Value[0];
-                var y = kvp.Value[1];
-                var im2 = im.Gravity(direction, 3, 3);
-                Assert.Equal(new double[] { 255 }, im2[x, y]);
-                Assert.Equal(255.0 / 9.0, im2.Avg());
-            }
+        var im = Image.Black(1, 1) + 255;
+        var positions = new Dictionary
+        {
+            {Enums.CompassDirection.Centre, new[] {1, 1}},
+            {Enums.CompassDirection.North, new[] {1, 0}},
+            {Enums.CompassDirection.South, new[] {1, 2}},
+            {Enums.CompassDirection.East, new[] {2, 1}},
+            {Enums.CompassDirection.West, new[] {0, 1}},
+            {Enums.CompassDirection.NorthEast, new[] {2, 0}},
+            {Enums.CompassDirection.SouthEast, new[] {2, 2}},
+            {Enums.CompassDirection.SouthWest, new[] {0, 2}},
+            {Enums.CompassDirection.NorthWest, new[] {0, 0}}
+        };
+
+        foreach (var kvp in positions)
+        {
+            var direction = kvp.Key;
+            var x = kvp.Value[0];
+            var y = kvp.Value[1];
+            var im2 = im.Gravity(direction, 3, 3);
+            Assert.Equal(new double[] { 255 }, im2[x, y]);
+            Assert.Equal(255.0 / 9.0, im2.Avg());
         }
+    }
 
-        [Fact]
-        public void TestExtract()
+    [Fact]
+    public void TestExtract()
+    {
+        foreach (var fmt in Helper.AllFormats)
         {
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var test = _colour.Cast(fmt);
+            var test = _colour.Cast(fmt);
 
-                var pixel = test[30, 30];
-                Assert.Equal(new double[] { 2, 3, 4 }, pixel);
+            var pixel = test[30, 30];
+            Assert.Equal(new double[] { 2, 3, 4 }, pixel);
 
-                var sub = test.ExtractArea(25, 25, 10, 10);
+            var sub = test.ExtractArea(25, 25, 10, 10);
 
-                pixel = sub[5, 5];
-                Assert.Equal(new double[] { 2, 3, 4 }, pixel);
+            pixel = sub[5, 5];
+            Assert.Equal(new double[] { 2, 3, 4 }, pixel);
 
-                sub = test.ExtractBand(1, n: 2);
+            sub = test.ExtractBand(1, n: 2);
 
-                pixel = sub[30, 30];
-                Assert.Equal(new double[] { 3, 4 }, pixel);
-            }
+            pixel = sub[30, 30];
+            Assert.Equal(new double[] { 3, 4 }, pixel);
         }
+    }
 
-        [Fact]
-        public void TestSlice()
-        {
-            var test = _colour;
-            var bands = test.Bandsplit().Select(i => i.Avg()).ToArray();
+    [Fact]
+    public void TestSlice()
+    {
+        var test = _colour;
+        var bands = test.Bandsplit().Select(i => i.Avg()).ToArray();
 
-            var x = test[0].Avg();
-            Assert.Equal(bands[0], x);
+        var x = test[0].Avg();
+        Assert.Equal(bands[0], x);
 
-            // [-1]
-            x = test[test.Bands - 1].Avg();
-            Assert.Equal(bands[2], x);
+        // [-1]
+        x = test[test.Bands - 1].Avg();
+        Assert.Equal(bands[2], x);
 
-            // [1:3]
-            x = test.ExtractBand(1, n: 2).Avg();
-            Assert.Equal(bands.Skip(1).Take(2).Average(), x);
+        // [1:3]
+        x = test.ExtractBand(1, n: 2).Avg();
+        Assert.Equal(bands.Skip(1).Take(2).Average(), x);
 
-            // [1:-1]
-            x = test.ExtractBand(1, n: test.Bands - 1).Avg();
-            Assert.Equal(bands.Skip(1).Take(test.Bands - 1).Average(), x);
+        // [1:-1]
+        x = test.ExtractBand(1, n: test.Bands - 1).Avg();
+        Assert.Equal(bands.Skip(1).Take(test.Bands - 1).Average(), x);
 
-            // [:2]
-            x = test.ExtractBand(0, n: 2).Avg();
-            Assert.Equal(bands.Take(2).Average(), x);
+        // [:2]
+        x = test.ExtractBand(0, n: 2).Avg();
+        Assert.Equal(bands.Take(2).Average(), x);
 
-            // [1:]
-            x = test.ExtractBand(1, n: test.Bands - 1).Avg();
-            Assert.Equal(bands.Skip(1).Take(test.Bands - 1).Average(), x);
+        // [1:]
+        x = test.ExtractBand(1, n: test.Bands - 1).Avg();
+        Assert.Equal(bands.Skip(1).Take(test.Bands - 1).Average(), x);
 
-            // [-1]
-            x = test[test.Bands - 1].Avg();
-            Assert.Equal(bands[test.Bands - 1], x);
-        }
+        // [-1]
+        x = test[test.Bands - 1].Avg();
+        Assert.Equal(bands[test.Bands - 1], x);
+    }
 
-        [Fact]
-        public void TestCrop()
+    [Fact]
+    public void TestCrop()
+    {
+        foreach (var fmt in Helper.AllFormats)
         {
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var test = _colour.Cast(fmt);
-                var pixel = test[30, 30];
-                Assert.Equal(new double[] { 2, 3, 4 }, pixel);
-                var sub = test.Crop(25, 25, 10, 10);
-                pixel = sub[5, 5];
-                Assert.Equal(new double[] { 2, 3, 4 }, pixel);
-            }
+            var test = _colour.Cast(fmt);
+            var pixel = test[30, 30];
+            Assert.Equal(new double[] { 2, 3, 4 }, pixel);
+            var sub = test.Crop(25, 25, 10, 10);
+            pixel = sub[5, 5];
+            Assert.Equal(new double[] { 2, 3, 4 }, pixel);
         }
+    }
 
-        [SkippableFact]
-        public void TestSmartcrop()
-        {
-            Skip.IfNot(Helper.Have("smartcrop"), "no smartcrop, skipping test");
+    [SkippableFact]
+    public void TestSmartcrop()
+    {
+        Skip.IfNot(Helper.Have("smartcrop"), "no smartcrop, skipping test");
 
-            var test = _image.Smartcrop(100, 100);
-            Assert.Equal(100, test.Width);
-            Assert.Equal(100, test.Height);
-        }
+        var test = _image.Smartcrop(100, 100);
+        Assert.Equal(100, test.Width);
+        Assert.Equal(100, test.Height);
+    }
 
-        [Fact]
-        public void TestFalsecolour()
+    [Fact]
+    public void TestFalsecolour()
+    {
+        foreach (var fmt in Helper.AllFormats)
         {
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var test = _colour.Cast(fmt);
+            var test = _colour.Cast(fmt);
 
-                var im = test.Falsecolour();
+            var im = test.Falsecolour();
 
-                Assert.Equal(test.Width, im.Width);
-                Assert.Equal(test.Height, im.Height);
-                Assert.Equal(3, im.Bands);
+            Assert.Equal(test.Width, im.Width);
+            Assert.Equal(test.Height, im.Height);
+            Assert.Equal(3, im.Bands);
 
-                var pixel = im[30, 30];
-                Assert.Equal(new double[] { 20, 0, 41 }, pixel);
-            }
+            var pixel = im[30, 30];
+            Assert.Equal(new double[] { 20, 0, 41 }, pixel);
         }
+    }
 
-        [Fact]
-        public void TestFlatten()
+    [Fact]
+    public void TestFlatten()
+    {
+        const int mx = 255;
+        const double alpha = mx / 2.0;
+        const double nalpha = mx - alpha;
+
+        foreach (var fmt in Helper.UnsignedFormats
+                     .Concat(new[] { Enums.BandFormat.Short, Enums.BandFormat.Int })
+                     .Concat(Helper.FloatFormats))
         {
-            const int mx = 255;
-            const double alpha = mx / 2.0;
-            const double nalpha = mx - alpha;
+            var test = _colour.Bandjoin(alpha).Cast(fmt);
+            var pixel = test[30, 30];
 
-            foreach (var fmt in Helper.UnsignedFormats
-                .Concat(new[] { Enums.BandFormat.Short, Enums.BandFormat.Int })
-                .Concat(Helper.FloatFormats))
-            {
-                var test = _colour.Bandjoin(alpha).Cast(fmt);
-                var pixel = test[30, 30];
+            var predict = pixel.Take(pixel.Length - 1)
+                .Select(x => Convert.ToInt32(x) * alpha / mx)
+                .ToArray();
 
-                var predict = pixel.Take(pixel.Length - 1)
-                    .Select(x => Convert.ToInt32(x) * alpha / mx)
-                    .ToArray();
+            var im = test.Flatten();
 
-                var im = test.Flatten();
+            Assert.Equal(3, im.Bands);
+            pixel = im[30, 30];
+            foreach (var zip in pixel.Zip(predict, (d, d1) => new[] { d, d1 }))
+            {
+                var x = zip[0];
+                var y = zip[1];
 
-                Assert.Equal(3, im.Bands);
-                pixel = im[30, 30];
-                foreach (var zip in pixel.Zip(predict, (d, d1) => new[] { d, d1 }))
-                {
-                    var x = zip[0];
-                    var y = zip[1];
+                // we use float arithetic for int and uint, so the rounding
+                // differs ... don't require huge accuracy
+                Assert.True(Math.Abs(x - y) < 2);
+            }
 
-                    // we use float arithetic for int and uint, so the rounding
-                    // differs ... don't require huge accuracy
-                    Assert.True(Math.Abs(x - y) < 2);
-                }
+            im = test.Flatten(background: new double[] { 100, 100, 100 });
 
-                im = test.Flatten(background: new double[] { 100, 100, 100 });
+            pixel = test[30, 30];
+            predict = pixel.Take(pixel.Length - 1)
+                .Select(x => Convert.ToInt32(x) * alpha / mx + 100 * nalpha / mx)
+                .ToArray();
 
-                pixel = test[30, 30];
-                predict = pixel.Take(pixel.Length - 1)
-                    .Select(x => Convert.ToInt32(x) * alpha / mx + 100 * nalpha / mx)
-                    .ToArray();
-
-                Assert.Equal(3, im.Bands);
-                pixel = im[30, 30];
-                foreach (var zip in pixel.Zip(predict, (d, d1) => new[] { d, d1 }))
-                {
-                    var x = zip[0];
-                    var y = zip[1];
+            Assert.Equal(3, im.Bands);
+            pixel = im[30, 30];
+            foreach (var zip in pixel.Zip(predict, (d, d1) => new[] { d, d1 }))
+            {
+                var x = zip[0];
+                var y = zip[1];
 
-                    Assert.True(Math.Abs(x - y) < 2);
-                }
+                Assert.True(Math.Abs(x - y) < 2);
             }
         }
+    }
+
+    [Fact]
+    public void TestPremultiply()
+    {
+        const int mx = 255;
+        const double alpha = mx / 2.0;
 
-        [Fact]
-        public void TestPremultiply()
+        foreach (var fmt in Helper.UnsignedFormats
+                     .Concat(new[] { Enums.BandFormat.Short, Enums.BandFormat.Int })
+                     .Concat(Helper.FloatFormats))
         {
-            const int mx = 255;
-            const double alpha = mx / 2.0;
+            var test = _colour.Bandjoin(alpha).Cast(fmt);
+            var pixel = test[30, 30];
 
-            foreach (var fmt in Helper.UnsignedFormats
-                .Concat(new[] { Enums.BandFormat.Short, Enums.BandFormat.Int })
-                .Concat(Helper.FloatFormats))
-            {
-                var test = _colour.Bandjoin(alpha).Cast(fmt);
-                var pixel = test[30, 30];
+            var predict = pixel.Take(pixel.Length - 1)
+                .Select(x => Convert.ToInt32(x) * alpha / mx)
+                .Concat(new[] { alpha })
+                .ToArray();
 
-                var predict = pixel.Take(pixel.Length - 1)
-                    .Select(x => Convert.ToInt32(x) * alpha / mx)
-                    .Concat(new[] { alpha })
-                    .ToArray();
+            var im = test.Premultiply();
 
-                var im = test.Premultiply();
-
-                Assert.Equal(test.Bands, im.Bands);
-                pixel = im[30, 30];
-                foreach (var zip in pixel.Zip(predict, (d, d1) => new[] { d, d1 }))
-                {
-                    var x = zip[0];
-                    var y = zip[1];
+            Assert.Equal(test.Bands, im.Bands);
+            pixel = im[30, 30];
+            foreach (var zip in pixel.Zip(predict, (d, d1) => new[] { d, d1 }))
+            {
+                var x = zip[0];
+                var y = zip[1];
 
-                    // we use float arithetic for int and uint, so the rounding
-                    // differs ... don't require huge accuracy
-                    Assert.True(Math.Abs(x - y) < 2);
-                }
+                // we use float arithetic for int and uint, so the rounding
+                // differs ... don't require huge accuracy
+                Assert.True(Math.Abs(x - y) < 2);
             }
         }
+    }
 
-        [SkippableFact]
-        public void TestComposite()
-        {
-            Skip.IfNot(Helper.Have("composite"), "no composite support, skipping test");
+    [SkippableFact]
+    public void TestComposite()
+    {
+        Skip.IfNot(Helper.Have("composite"), "no composite support, skipping test");
 
-            // 50% transparent image
-            var overlay = _colour.Bandjoin(128);
-            var baseImage = _colour + 100;
-            var comp = baseImage.Composite(overlay, Enums.BlendMode.Over);
+        // 50% transparent image
+        var overlay = _colour.Bandjoin(128);
+        var baseImage = _colour + 100;
+        var comp = baseImage.Composite(overlay, Enums.BlendMode.Over);
 
-            Helper.AssertAlmostEqualObjects(new[] { 51.8, 52.8, 53.8, 255 }, comp[0, 0], 0.1);
-        }
+        Helper.AssertAlmostEqualObjects(new[] { 51.8, 52.8, 53.8, 255 }, comp[0, 0], 0.1);
+    }
 
-        [Fact]
-        public void TestUnpremultiply()
-        {
-            const int mx = 255;
-            const double alpha = mx / 2.0;
+    [Fact]
+    public void TestUnpremultiply()
+    {
+        const int mx = 255;
+        const double alpha = mx / 2.0;
 
-            foreach (var fmt in Helper.UnsignedFormats
-                .Concat(new[] { Enums.BandFormat.Short, Enums.BandFormat.Int })
-                .Concat(Helper.FloatFormats))
-            {
-                var test = _colour.Bandjoin(alpha).Cast(fmt);
-                var pixel = test[30, 30];
+        foreach (var fmt in Helper.UnsignedFormats
+                     .Concat(new[] { Enums.BandFormat.Short, Enums.BandFormat.Int })
+                     .Concat(Helper.FloatFormats))
+        {
+            var test = _colour.Bandjoin(alpha).Cast(fmt);
+            var pixel = test[30, 30];
 
-                var predict = pixel.Take(pixel.Length - 1)
-                    .Select(x => Convert.ToInt32(x) / (alpha / mx))
-                    .Concat(new[] { alpha })
-                    .ToArray();
+            var predict = pixel.Take(pixel.Length - 1)
+                .Select(x => Convert.ToInt32(x) / (alpha / mx))
+                .Concat(new[] { alpha })
+                .ToArray();
 
-                var im = test.Unpremultiply();
+            var im = test.Unpremultiply();
 
-                Assert.Equal(test.Bands, im.Bands);
-                pixel = im[30, 30];
-                foreach (var zip in pixel.Zip(predict, (d, d1) => new[] { d, d1 }))
-                {
-                    var x = zip[0];
-                    var y = zip[1];
+            Assert.Equal(test.Bands, im.Bands);
+            pixel = im[30, 30];
+            foreach (var zip in pixel.Zip(predict, (d, d1) => new[] { d, d1 }))
+            {
+                var x = zip[0];
+                var y = zip[1];
 
-                    // we use float arithetic for int and uint, so the rounding
-                    // differs ... don't require huge accuracy
-                    Assert.True(Math.Abs(x - y) < 2);
-                }
+                // we use float arithetic for int and uint, so the rounding
+                // differs ... don't require huge accuracy
+                Assert.True(Math.Abs(x - y) < 2);
             }
         }
+    }
 
-        [Fact]
-        public void TestFlip()
+    [Fact]
+    public void TestFlip()
+    {
+        foreach (var fmt in Helper.AllFormats)
         {
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var test = _colour.Cast(fmt);
+            var test = _colour.Cast(fmt);
 
-                var result = test.FlipHor();
-                result = result.FlipVer();
-                result = result.FlipHor();
-                result = result.FlipVer();
+            var result = test.FlipHor();
+            result = result.FlipVer();
+            result = result.FlipHor();
+            result = result.FlipVer();
 
-                var diff = (test - result).Abs().Max();
+            var diff = (test - result).Abs().Max();
 
-                Assert.Equal(0, diff);
-            }
+            Assert.Equal(0, diff);
         }
+    }
 
-        [Fact]
-        public void TestGamma()
+    [Fact]
+    public void TestGamma()
+    {
+        const double exponent = 2.4;
+        foreach (var fmt in Helper.NonComplexFormats)
         {
-            const double exponent = 2.4;
-            foreach (var fmt in Helper.NonComplexFormats)
+            var mx = Helper.MaxValue[fmt];
+            var test = (_colour + mx / 2.0).Cast(fmt);
+
+            var norm = Math.Pow(mx, exponent) / mx;
+            var result = test.Gamma();
+            var before = test[30, 30];
+            var after = result[30, 30];
+            var predict = before.Select(x => Math.Pow(x, exponent) / norm);
+            foreach (var zip in after.Zip(predict, (d, d1) => new[] { d, d1 }))
             {
-                var mx = Helper.MaxValue[fmt];
-                var test = (_colour + mx / 2.0).Cast(fmt);
-
-                var norm = Math.Pow(mx, exponent) / mx;
-                var result = test.Gamma();
-                var before = test[30, 30];
-                var after = result[30, 30];
-                var predict = before.Select(x => Math.Pow(x, exponent) / norm);
-                foreach (var zip in after.Zip(predict, (d, d1) => new[] { d, d1 }))
-                {
-                    var a = zip[0];
-                    var b = zip[1];
+                var a = zip[0];
+                var b = zip[1];
 
-                    // ie. less than 1% error, rounding on 7-bit images
-                    // means this is all we can expect
-                    Assert.True(Math.Abs(a - b) < mx / 100.0);
-                }
+                // ie. less than 1% error, rounding on 7-bit images
+                // means this is all we can expect
+                Assert.True(Math.Abs(a - b) < mx / 100.0);
             }
+        }
 
-            const double exponent2 = 1.2;
-            foreach (var fmt in Helper.NonComplexFormats)
+        const double exponent2 = 1.2;
+        foreach (var fmt in Helper.NonComplexFormats)
+        {
+            var mx = Helper.MaxValue[fmt];
+            var test = (_colour + mx / 2.0).Cast(fmt);
+
+            var norm = Math.Pow(mx, exponent2) / mx;
+            var result = test.Gamma(exponent: 1.0 / exponent2);
+            var before = test[30, 30];
+            var after = result[30, 30];
+            var predict = before.Select(x => Math.Pow(x, exponent2) / norm);
+            foreach (var zip in after.Zip(predict, (d, d1) => new[] { d, d1 }))
             {
-                var mx = Helper.MaxValue[fmt];
-                var test = (_colour + mx / 2.0).Cast(fmt);
-
-                var norm = Math.Pow(mx, exponent2) / mx;
-                var result = test.Gamma(exponent: 1.0 / exponent2);
-                var before = test[30, 30];
-                var after = result[30, 30];
-                var predict = before.Select(x => Math.Pow(x, exponent2) / norm);
-                foreach (var zip in after.Zip(predict, (d, d1) => new[] { d, d1 }))
-                {
-                    var a = zip[0];
-                    var b = zip[1];
+                var a = zip[0];
+                var b = zip[1];
 
-                    // ie. less than 1% error, rounding on 7-bit images
-                    // means this is all we can expect
-                    Assert.True(Math.Abs(a - b) < mx / 100.0);
-                }
+                // ie. less than 1% error, rounding on 7-bit images
+                // means this is all we can expect
+                Assert.True(Math.Abs(a - b) < mx / 100.0);
             }
         }
+    }
 
-        [Fact]
-        public void TestGrid()
-        {
-            var test = _colour.Replicate(1, 12);
-            Assert.Equal(_colour.Width, test.Width);
-            Assert.Equal(_colour.Height * 12, test.Height);
+    [Fact]
+    public void TestGrid()
+    {
+        var test = _colour.Replicate(1, 12);
+        Assert.Equal(_colour.Width, test.Width);
+        Assert.Equal(_colour.Height * 12, test.Height);
 
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var im = test.Cast(fmt);
-                var result = im.Grid(test.Width, 3, 4);
-                Assert.Equal(_colour.Width * 3, result.Width);
-                Assert.Equal(_colour.Height * 4, result.Height);
-
-                var before = im[10, 10];
-                var after = result[10 + test.Width * 2, 10 + test.Width * 2];
-                Assert.Equal(before, after);
-
-                before = im[50, 50];
-                after = result[50 + test.Width * 2, 50 + test.Width * 2];
-                Assert.Equal(before, after);
-            }
+        foreach (var fmt in Helper.AllFormats)
+        {
+            var im = test.Cast(fmt);
+            var result = im.Grid(test.Width, 3, 4);
+            Assert.Equal(_colour.Width * 3, result.Width);
+            Assert.Equal(_colour.Height * 4, result.Height);
+
+            var before = im[10, 10];
+            var after = result[10 + test.Width * 2, 10 + test.Width * 2];
+            Assert.Equal(before, after);
+
+            before = im[50, 50];
+            after = result[50 + test.Width * 2, 50 + test.Width * 2];
+            Assert.Equal(before, after);
         }
+    }
 
-        [Fact]
-        public void TestIfthenelse()
+    [Fact]
+    public void TestIfthenelse()
+    {
+        var test = _mono > 3;
+        foreach (var x in Helper.AllFormats)
         {
-            var test = _mono > 3;
-            foreach (var x in Helper.AllFormats)
+            foreach (var y in Helper.AllFormats)
             {
-                foreach (var y in Helper.AllFormats)
-                {
-                    var t = (_colour + 10).Cast(x);
-                    var e = _colour.Cast(y);
-                    var r = test.Ifthenelse(t, e);
+                var t = (_colour + 10).Cast(x);
+                var e = _colour.Cast(y);
+                var r = test.Ifthenelse(t, e);
 
-                    Assert.Equal(_colour.Width, r.Width);
-                    Assert.Equal(_colour.Height, r.Height);
-                    Assert.Equal(_colour.Bands, r.Bands);
+                Assert.Equal(_colour.Width, r.Width);
+                Assert.Equal(_colour.Height, r.Height);
+                Assert.Equal(_colour.Bands, r.Bands);
 
-                    var predict = e[10, 10];
-                    var result = r[10, 10];
-                    Assert.Equal(predict, result);
+                var predict = e[10, 10];
+                var result = r[10, 10];
+                Assert.Equal(predict, result);
 
-                    predict = t[50, 50];
-                    result = r[50, 50];
-                    Assert.Equal(predict, result);
-                }
+                predict = t[50, 50];
+                result = r[50, 50];
+                Assert.Equal(predict, result);
             }
+        }
 
-            test = _colour > 3;
-            foreach (var x in Helper.AllFormats)
+        test = _colour > 3;
+        foreach (var x in Helper.AllFormats)
+        {
+            foreach (var y in Helper.AllFormats)
             {
-                foreach (var y in Helper.AllFormats)
-                {
-                    var t = (_mono + 10).Cast(x);
-                    var e = _mono.Cast(y);
-                    var r = test.Ifthenelse(t, e);
-
-                    Assert.Equal(_colour.Width, r.Width);
-                    Assert.Equal(_colour.Height, r.Height);
-                    Assert.Equal(_colour.Bands, r.Bands);
-
-                    var cp = test[10, 10];
-                    var tp = Enumerable.Repeat(t[10, 10][0], 3).ToArray();
-                    var ep = Enumerable.Repeat(e[10, 10][0], 3).ToArray();
-                    var predict = cp
-                        .Zip(tp, (e1, e2) => new { e1, e2 })
-                        .Zip(ep, (z1, e3) => Tuple.Create(z1.e1, z1.e2, e3))
-                        .Select(tuple => Convert.ToInt32(tuple.Item1) != 0 ? tuple.Item2 : tuple.Item3)
-                        .ToArray();
-                    var result = r[10, 10];
-                    Assert.Equal(predict, result);
-
-                    cp = test[50, 50];
-                    tp = Enumerable.Repeat(t[50, 50][0], 3).ToArray();
-                    ep = Enumerable.Repeat(e[50, 50][0], 3).ToArray();
-                    predict = cp
-                        .Zip(tp, (e1, e2) => new { e1, e2 })
-                        .Zip(ep, (z1, e3) => Tuple.Create(z1.e1, z1.e2, e3))
-                        .Select(tuple => Convert.ToInt32(tuple.Item1) != 0 ? tuple.Item2 : tuple.Item3)
-                        .ToArray();
-                    result = r[50, 50];
-                    Assert.Equal(predict, result);
-                }
+                var t = (_mono + 10).Cast(x);
+                var e = _mono.Cast(y);
+                var r = test.Ifthenelse(t, e);
+
+                Assert.Equal(_colour.Width, r.Width);
+                Assert.Equal(_colour.Height, r.Height);
+                Assert.Equal(_colour.Bands, r.Bands);
+
+                var cp = test[10, 10];
+                var tp = Enumerable.Repeat(t[10, 10][0], 3).ToArray();
+                var ep = Enumerable.Repeat(e[10, 10][0], 3).ToArray();
+                var predict = cp
+                    .Zip(tp, (e1, e2) => new { e1, e2 })
+                    .Zip(ep, (z1, e3) => Tuple.Create(z1.e1, z1.e2, e3))
+                    .Select(tuple => Convert.ToInt32(tuple.Item1) != 0 ? tuple.Item2 : tuple.Item3)
+                    .ToArray();
+                var result = r[10, 10];
+                Assert.Equal(predict, result);
+
+                cp = test[50, 50];
+                tp = Enumerable.Repeat(t[50, 50][0], 3).ToArray();
+                ep = Enumerable.Repeat(e[50, 50][0], 3).ToArray();
+                predict = cp
+                    .Zip(tp, (e1, e2) => new { e1, e2 })
+                    .Zip(ep, (z1, e3) => Tuple.Create(z1.e1, z1.e2, e3))
+                    .Select(tuple => Convert.ToInt32(tuple.Item1) != 0 ? tuple.Item2 : tuple.Item3)
+                    .ToArray();
+                result = r[50, 50];
+                Assert.Equal(predict, result);
             }
+        }
 
-            test = _colour > 3;
-            foreach (var x in Helper.AllFormats)
+        test = _colour > 3;
+        foreach (var x in Helper.AllFormats)
+        {
+            foreach (var y in Helper.AllFormats)
             {
-                foreach (var y in Helper.AllFormats)
-                {
-                    var t = (_mono + 10).Cast(x);
-                    var e = _mono.Cast(y);
-                    var r = test.Ifthenelse(t, e, blend: true);
+                var t = (_mono + 10).Cast(x);
+                var e = _mono.Cast(y);
+                var r = test.Ifthenelse(t, e, blend: true);
 
-                    Assert.Equal(_colour.Width, r.Width);
-                    Assert.Equal(_colour.Height, r.Height);
-                    Assert.Equal(_colour.Bands, r.Bands);
+                Assert.Equal(_colour.Width, r.Width);
+                Assert.Equal(_colour.Height, r.Height);
+                Assert.Equal(_colour.Bands, r.Bands);
 
-                    var result = r[10, 10];
-                    Assert.Equal(new double[] { 3, 3, 13 }, result);
-                }
+                var result = r[10, 10];
+                Assert.Equal(new double[] { 3, 3, 13 }, result);
             }
-
-            test = _mono > 3;
-            var r2 = test.Ifthenelse(new[] { 1, 2, 3 }, _colour);
-            Assert.Equal(_colour.Width, r2.Width);
-            Assert.Equal(_colour.Height, r2.Height);
-            Assert.Equal(_colour.Bands, r2.Bands);
-            Assert.Equal(_colour.Format, r2.Format);
-            Assert.Equal(_colour.Format, r2.Format);
-            Assert.Equal(_colour.Interpretation, r2.Interpretation);
-            var result2 = r2[10, 10];
-            Assert.Equal(new double[] { 2, 3, 4 }, result2);
-            result2 = r2[50, 50];
-            Assert.Equal(new double[] { 1, 2, 3 }, result2);
-
-            test = _mono;
-            r2 = test.Ifthenelse(new[] { 1, 2, 3 }, _colour, blend: true);
-            Assert.Equal(_colour.Width, r2.Width);
-            Assert.Equal(_colour.Height, r2.Height);
-            Assert.Equal(_colour.Bands, r2.Bands);
-            Assert.Equal(_colour.Format, r2.Format);
-            Assert.Equal(_colour.Format, r2.Format);
-            Assert.Equal(_colour.Interpretation, r2.Interpretation);
-            result2 = r2[10, 10];
-            Helper.AssertAlmostEqualObjects(new double[] { 2, 3, 4 }, result2, 0.1);
-            result2 = r2[50, 50];
-            Helper.AssertAlmostEqualObjects(new[] { 3.0, 4.9, 6.9 }, result2, 0.1);
         }
 
-        [SkippableFact]
-        public void TestSwitch()
-        {
-            // switch was added in libvips 8.9.
-            Skip.IfNot(NetVips.AtLeastLibvips(8, 9), "requires libvips >= 8.9");
-
-            var x = Image.Grey(256, 256, uchar: true);
-
-            // slice into two at 128, we should get 50% of pixels in each half
-            var index = Image.Switch(x < 128, x >= 128);
-            Assert.Equal(0.5, index.Avg());
-
-            // slice into four
-            index = Image.Switch(
-                x < 64,
-                x >= 64 && x < 128,
-                x >= 128 && x < 192,
-                x >= 192
-            );
-            Assert.Equal(1.5, index.Avg());
-
-            // no match should return n + 1
-            index = Image.Switch(x.Equal(1000), x.Equal(2000));
-            Assert.Equal(2, index.Avg());
-        }
+        test = _mono > 3;
+        var r2 = test.Ifthenelse(new[] { 1, 2, 3 }, _colour);
+        Assert.Equal(_colour.Width, r2.Width);
+        Assert.Equal(_colour.Height, r2.Height);
+        Assert.Equal(_colour.Bands, r2.Bands);
+        Assert.Equal(_colour.Format, r2.Format);
+        Assert.Equal(_colour.Format, r2.Format);
+        Assert.Equal(_colour.Interpretation, r2.Interpretation);
+        var result2 = r2[10, 10];
+        Assert.Equal(new double[] { 2, 3, 4 }, result2);
+        result2 = r2[50, 50];
+        Assert.Equal(new double[] { 1, 2, 3 }, result2);
+
+        test = _mono;
+        r2 = test.Ifthenelse(new[] { 1, 2, 3 }, _colour, blend: true);
+        Assert.Equal(_colour.Width, r2.Width);
+        Assert.Equal(_colour.Height, r2.Height);
+        Assert.Equal(_colour.Bands, r2.Bands);
+        Assert.Equal(_colour.Format, r2.Format);
+        Assert.Equal(_colour.Format, r2.Format);
+        Assert.Equal(_colour.Interpretation, r2.Interpretation);
+        result2 = r2[10, 10];
+        Helper.AssertAlmostEqualObjects(new double[] { 2, 3, 4 }, result2, 0.1);
+        result2 = r2[50, 50];
+        Helper.AssertAlmostEqualObjects(new[] { 3.0, 4.9, 6.9 }, result2, 0.1);
+    }
 
-        [Fact]
-        public void TestInsert()
+    [SkippableFact]
+    public void TestSwitch()
+    {
+        // switch was added in libvips 8.9.
+        Skip.IfNot(NetVips.AtLeastLibvips(8, 9), "requires libvips >= 8.9");
+
+        var x = Image.Grey(256, 256, uchar: true);
+
+        // slice into two at 128, we should get 50% of pixels in each half
+        var index = Image.Switch(x < 128, x >= 128);
+        Assert.Equal(0.5, index.Avg());
+
+        // slice into four
+        index = Image.Switch(
+            x < 64,
+            x >= 64 && x < 128,
+            x >= 128 && x < 192,
+            x >= 192
+        );
+        Assert.Equal(1.5, index.Avg());
+
+        // no match should return n + 1
+        index = Image.Switch(x.Equal(1000), x.Equal(2000));
+        Assert.Equal(2, index.Avg());
+    }
+
+    [Fact]
+    public void TestInsert()
+    {
+        foreach (var x in Helper.AllFormats)
         {
-            foreach (var x in Helper.AllFormats)
+            foreach (var y in Helper.AllFormats)
             {
-                foreach (var y in Helper.AllFormats)
-                {
-                    var main = _mono.Cast(x);
-                    var sub = _colour.Cast(y);
-                    var r = main.Insert(sub, 10, 10);
+                var main = _mono.Cast(x);
+                var sub = _colour.Cast(y);
+                var r = main.Insert(sub, 10, 10);
 
-                    Assert.Equal(main.Width, r.Width);
-                    Assert.Equal(main.Height, r.Height);
-                    Assert.Equal(sub.Bands, r.Bands);
+                Assert.Equal(main.Width, r.Width);
+                Assert.Equal(main.Height, r.Height);
+                Assert.Equal(sub.Bands, r.Bands);
 
-                    var a = r[10, 10];
-                    var b = sub[0, 0];
-                    Assert.Equal(a, b);
+                var a = r[10, 10];
+                var b = sub[0, 0];
+                Assert.Equal(a, b);
 
-                    a = r[0, 0];
-                    b = Enumerable.Repeat(main[0, 0][0], 3).ToArray();
-                    Assert.Equal(a, b);
-                }
+                a = r[0, 0];
+                b = Enumerable.Repeat(main[0, 0][0], 3).ToArray();
+                Assert.Equal(a, b);
             }
+        }
 
-            foreach (var x in Helper.AllFormats)
+        foreach (var x in Helper.AllFormats)
+        {
+            foreach (var y in Helper.AllFormats)
             {
-                foreach (var y in Helper.AllFormats)
-                {
-                    var main = _mono.Cast(x);
-                    var sub = _colour.Cast(y);
-                    var r = main.Insert(sub, 10, 10, expand: true, background: new double[] { 100 });
+                var main = _mono.Cast(x);
+                var sub = _colour.Cast(y);
+                var r = main.Insert(sub, 10, 10, expand: true, background: new double[] { 100 });
 
-                    Assert.Equal(main.Width + 10, r.Width);
-                    Assert.Equal(main.Height + 10, r.Height);
-                    Assert.Equal(sub.Bands, r.Bands);
+                Assert.Equal(main.Width + 10, r.Width);
+                Assert.Equal(main.Height + 10, r.Height);
+                Assert.Equal(sub.Bands, r.Bands);
 
-                    var a = r[r.Width - 5, 5];
-                    Assert.Equal(new double[] { 100, 100, 100 }, a);
-                }
+                var a = r[r.Width - 5, 5];
+                Assert.Equal(new double[] { 100, 100, 100 }, a);
             }
         }
+    }
 
-        [Fact]
-        public void TestArrayjoin()
+    [Fact]
+    public void TestArrayjoin()
+    {
+        var maxWidth = 0;
+        var maxHeight = 0;
+        var maxBands = 0;
+        foreach (var image in _allImages)
         {
-            var maxWidth = 0;
-            var maxHeight = 0;
-            var maxBands = 0;
-            foreach (var image in _allImages)
+            if (image.Width > maxWidth)
             {
-                if (image.Width > maxWidth)
-                {
-                    maxWidth = image.Width;
-                }
+                maxWidth = image.Width;
+            }
 
-                if (image.Height > maxHeight)
-                {
-                    maxHeight = image.Height;
-                }
+            if (image.Height > maxHeight)
+            {
+                maxHeight = image.Height;
+            }
 
-                if (image.Bands > maxBands)
-                {
-                    maxBands = image.Bands;
-                }
+            if (image.Bands > maxBands)
+            {
+                maxBands = image.Bands;
             }
+        }
 
-            var im = Image.Arrayjoin(_allImages);
-            Assert.Equal(maxWidth * _allImages.Length, im.Width);
-            Assert.Equal(maxHeight, im.Height);
-            Assert.Equal(maxBands, im.Bands);
+        var im = Image.Arrayjoin(_allImages);
+        Assert.Equal(maxWidth * _allImages.Length, im.Width);
+        Assert.Equal(maxHeight, im.Height);
+        Assert.Equal(maxBands, im.Bands);
 
-            im = Image.Arrayjoin(_allImages, across: 1);
+        im = Image.Arrayjoin(_allImages, across: 1);
 
-            Assert.Equal(maxWidth, im.Width);
-            Assert.Equal(maxHeight * _allImages.Length, im.Height);
-            Assert.Equal(maxBands, im.Bands);
+        Assert.Equal(maxWidth, im.Width);
+        Assert.Equal(maxHeight * _allImages.Length, im.Height);
+        Assert.Equal(maxBands, im.Bands);
 
-            im = Image.Arrayjoin(_allImages, shim: 10);
+        im = Image.Arrayjoin(_allImages, shim: 10);
 
-            Assert.Equal(maxWidth * _allImages.Length + 10 * (_allImages.Length - 1), im.Width);
-            Assert.Equal(maxHeight, im.Height);
-            Assert.Equal(maxBands, im.Bands);
-        }
+        Assert.Equal(maxWidth * _allImages.Length + 10 * (_allImages.Length - 1), im.Width);
+        Assert.Equal(maxHeight, im.Height);
+        Assert.Equal(maxBands, im.Bands);
+    }
 
-        [Fact]
-        public void TestMsb()
+    [Fact]
+    public void TestMsb()
+    {
+        foreach (var fmt in Helper.UnsignedFormats)
         {
-            foreach (var fmt in Helper.UnsignedFormats)
-            {
-                var mx = Helper.MaxValue[fmt];
-                var size = Helper.SizeOfFormat[fmt];
-                var test = (_colour + mx / 8.0).Cast(fmt);
-                var im = test.Msb();
-
-                var before = test[10, 10];
-                var predict = before.Select(x => (double)(Convert.ToInt32(x) >> (size - 1) * 8)).ToArray();
-                var result = im[10, 10];
-                Assert.Equal(predict, result);
-
-                before = test[50, 50];
-                predict = before.Select(x => (double)(Convert.ToInt32(x) >> (size - 1) * 8)).ToArray();
-                result = im[50, 50];
-                Assert.Equal(predict, result);
-            }
-
-            foreach (var fmt in Helper.SignedFormats)
-            {
-                var mx = Helper.MaxValue[fmt];
-                var size = Helper.SizeOfFormat[fmt];
-                var test = (_colour + mx / 8.0).Cast(fmt);
-                var im = test.Msb();
-
-                var before = test[10, 10];
-                var predict = before.Select(x => (double)(128 + (Convert.ToInt32(x) >> (size - 1) * 8))).ToArray();
-                var result = im[10, 10];
-                Assert.Equal(predict, result);
-
-                before = test[50, 50];
-                predict = before.Select(x => (double)(128 + (Convert.ToInt32(x) >> (size - 1) * 8))).ToArray();
-                result = im[50, 50];
-                Assert.Equal(predict, result);
-            }
+            var mx = Helper.MaxValue[fmt];
+            var size = Helper.SizeOfFormat[fmt];
+            var test = (_colour + mx / 8.0).Cast(fmt);
+            var im = test.Msb();
+
+            var before = test[10, 10];
+            var predict = before.Select(x => (double)(Convert.ToInt32(x) >> (size - 1) * 8)).ToArray();
+            var result = im[10, 10];
+            Assert.Equal(predict, result);
+
+            before = test[50, 50];
+            predict = before.Select(x => (double)(Convert.ToInt32(x) >> (size - 1) * 8)).ToArray();
+            result = im[50, 50];
+            Assert.Equal(predict, result);
+        }
 
-            foreach (var fmt in Helper.UnsignedFormats)
-            {
-                var mx = Helper.MaxValue[fmt];
-                var size = Helper.SizeOfFormat[fmt];
-                var test = (_colour + mx / 8.0).Cast(fmt);
-                var im = test.Msb(band: 1);
-
-                var before = test[10, 10][1];
-                var predict = Convert.ToInt32(before) >> (size - 1) * 8;
-                var result = im[10, 10][0];
-                Assert.Equal(predict, result);
+        foreach (var fmt in Helper.SignedFormats)
+        {
+            var mx = Helper.MaxValue[fmt];
+            var size = Helper.SizeOfFormat[fmt];
+            var test = (_colour + mx / 8.0).Cast(fmt);
+            var im = test.Msb();
+
+            var before = test[10, 10];
+            var predict = before.Select(x => (double)(128 + (Convert.ToInt32(x) >> (size - 1) * 8))).ToArray();
+            var result = im[10, 10];
+            Assert.Equal(predict, result);
+
+            before = test[50, 50];
+            predict = before.Select(x => (double)(128 + (Convert.ToInt32(x) >> (size - 1) * 8))).ToArray();
+            result = im[50, 50];
+            Assert.Equal(predict, result);
+        }
 
-                before = test[50, 50][1];
-                predict = Convert.ToInt32(before) >> (size - 1) * 8;
-                result = im[50, 50][0];
-                Assert.Equal(predict, result);
-            }
+        foreach (var fmt in Helper.UnsignedFormats)
+        {
+            var mx = Helper.MaxValue[fmt];
+            var size = Helper.SizeOfFormat[fmt];
+            var test = (_colour + mx / 8.0).Cast(fmt);
+            var im = test.Msb(band: 1);
+
+            var before = test[10, 10][1];
+            var predict = Convert.ToInt32(before) >> (size - 1) * 8;
+            var result = im[10, 10][0];
+            Assert.Equal(predict, result);
+
+            before = test[50, 50][1];
+            predict = Convert.ToInt32(before) >> (size - 1) * 8;
+            result = im[50, 50][0];
+            Assert.Equal(predict, result);
         }
+    }
 
-        [Fact]
-        public void TestRecomb()
+    [Fact]
+    public void TestRecomb()
+    {
+        var array = new[]
         {
-            var array = new[]
-            {
-                0.2, 0.5, 0.3
-            };
+            0.2, 0.5, 0.3
+        };
 
-            dynamic Recomb(dynamic x)
+        dynamic Recomb(dynamic x)
+        {
+            if (x is Image image)
             {
-                if (x is Image image)
-                {
-                    return image.Recomb(Image.NewFromArray(new[] { array }));
-                }
-
-                var sum = array
-                    .Zip((IEnumerable)x, (d, o) => new[] { d, o })
-                    .Select(zip => zip[0] * zip[1])
-                    .Sum();
-
-                return new[]
-                {
-                    sum
-                };
+                return image.Recomb(Image.NewFromArray(new[] { array }));
             }
 
-            RunUnary(new[] { _colour }, Recomb, Helper.NonComplexFormats);
+            var sum = array
+                .Zip((IEnumerable)x, (d, o) => new[] { d, o })
+                .Select(zip => zip[0] * zip[1])
+                .Sum();
+
+            return new[]
+            {
+                sum
+            };
         }
 
-        [Fact]
-        public void TestReplicate()
+        RunUnary(new[] { _colour }, Recomb, Helper.NonComplexFormats);
+    }
+
+    [Fact]
+    public void TestReplicate()
+    {
+        foreach (var fmt in Helper.AllFormats)
         {
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var im = _colour.Cast(fmt);
+            var im = _colour.Cast(fmt);
 
-                var test = im.Replicate(10, 10);
-                Assert.Equal(_colour.Width * 10, test.Width);
-                Assert.Equal(_colour.Height * 10, test.Height);
+            var test = im.Replicate(10, 10);
+            Assert.Equal(_colour.Width * 10, test.Width);
+            Assert.Equal(_colour.Height * 10, test.Height);
 
-                var before = im[10, 10];
-                var after = test[10 + im.Width * 2, 10 + im.Width * 2];
-                Assert.Equal(before, after);
+            var before = im[10, 10];
+            var after = test[10 + im.Width * 2, 10 + im.Width * 2];
+            Assert.Equal(before, after);
 
-                before = im[50, 50];
-                after = test[50 + im.Width * 2, 50 + im.Width * 2];
-                Assert.Equal(before, after);
-            }
+            before = im[50, 50];
+            after = test[50 + im.Width * 2, 50 + im.Width * 2];
+            Assert.Equal(before, after);
         }
+    }
 
-        [Fact]
-        public void TestRot45()
+    [Fact]
+    public void TestRot45()
+    {
+        // test has a quarter-circle in the bottom right
+        var test = _colour.ExtractArea(0, 0, 51, 51);
+        foreach (var fmt in Helper.AllFormats)
         {
-            // test has a quarter-circle in the bottom right
-            var test = _colour.ExtractArea(0, 0, 51, 51);
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var im = test.Cast(fmt);
+            var im = test.Cast(fmt);
 
-                var im2 = im.Rot45();
-                var before = im[50, 50];
-                var after = im2[25, 50];
-                Assert.Equal(before, after);
+            var im2 = im.Rot45();
+            var before = im[50, 50];
+            var after = im2[25, 50];
+            Assert.Equal(before, after);
 
-                foreach (var zip in Helper.Rot45Angles.Zip(Helper.Rot45AngleBonds, (s, s1) => new[] { s, s1 }))
-                {
-                    var a = zip[0];
-                    var b = zip[1];
-                    im2 = im.Rot45(angle: a);
-                    var after2 = im2.Rot45(angle: b);
-                    var diff = (after2 - im).Abs().Max();
-                    Assert.Equal(0, diff);
-                }
+            foreach (var zip in Helper.Rot45Angles.Zip(Helper.Rot45AngleBonds, (s, s1) => new[] { s, s1 }))
+            {
+                var a = zip[0];
+                var b = zip[1];
+                im2 = im.Rot45(angle: a);
+                var after2 = im2.Rot45(angle: b);
+                var diff = (after2 - im).Abs().Max();
+                Assert.Equal(0, diff);
             }
         }
+    }
 
-        [Fact]
-        public void TestRot()
+    [Fact]
+    public void TestRot()
+    {
+        // test has a quarter-circle in the bottom right
+        var test = _colour.ExtractArea(0, 0, 51, 51);
+        foreach (var fmt in Helper.AllFormats)
         {
-            // test has a quarter-circle in the bottom right
-            var test = _colour.ExtractArea(0, 0, 51, 51);
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var im = test.Cast(fmt);
+            var im = test.Cast(fmt);
 
-                var im2 = im.Rot(Enums.Angle.D90);
-                var before = im[50, 50];
-                var after = im2[0, 50];
-                Assert.Equal(before, after);
+            var im2 = im.Rot(Enums.Angle.D90);
+            var before = im[50, 50];
+            var after = im2[0, 50];
+            Assert.Equal(before, after);
 
-                foreach (var zip in Helper.RotAngles.Zip(Helper.RotAngleBonds, (s, s1) => new[] { s, s1 }))
-                {
-                    var a = zip[0];
-                    var b = zip[1];
-                    im2 = im.Rot(a);
-                    var after2 = im2.Rot(b);
-                    var diff = (after2 - im).Abs().Max();
-                    Assert.Equal(0, diff);
-                }
+            foreach (var zip in Helper.RotAngles.Zip(Helper.RotAngleBonds, (s, s1) => new[] { s, s1 }))
+            {
+                var a = zip[0];
+                var b = zip[1];
+                im2 = im.Rot(a);
+                var after2 = im2.Rot(b);
+                var diff = (after2 - im).Abs().Max();
+                Assert.Equal(0, diff);
             }
         }
+    }
 
-        [Fact]
-        public void TestScaleImage()
+    [Fact]
+    public void TestScaleImage()
+    {
+        foreach (var fmt in Helper.NonComplexFormats)
         {
-            foreach (var fmt in Helper.NonComplexFormats)
-            {
-                var test = _colour.Cast(fmt);
+            var test = _colour.Cast(fmt);
 
-                var im = test.ScaleImage();
-                Assert.Equal(255, im.Max());
-                Assert.Equal(0, im.Min());
+            var im = test.ScaleImage();
+            Assert.Equal(255, im.Max());
+            Assert.Equal(0, im.Min());
 
-                im = test.ScaleImage(log: true);
-                Assert.Equal(255, im.Max());
-            }
+            im = test.ScaleImage(log: true);
+            Assert.Equal(255, im.Max());
         }
+    }
 
-        [Fact]
-        public void TestSubsample()
+    [Fact]
+    public void TestSubsample()
+    {
+        foreach (var fmt in Helper.AllFormats)
         {
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var test = _colour.Cast(fmt);
+            var test = _colour.Cast(fmt);
 
-                var im = test.Subsample(3, 3);
-                Assert.Equal(test.Width / 3, im.Width);
-                Assert.Equal(test.Height / 3, im.Height);
+            var im = test.Subsample(3, 3);
+            Assert.Equal(test.Width / 3, im.Width);
+            Assert.Equal(test.Height / 3, im.Height);
 
-                var before = test[60, 60];
-                var after = im[20, 20];
-                Assert.Equal(before, after);
-            }
+            var before = test[60, 60];
+            var after = im[20, 20];
+            Assert.Equal(before, after);
         }
+    }
 
-        [Fact]
-        public void TestZoom()
+    [Fact]
+    public void TestZoom()
+    {
+        foreach (var fmt in Helper.AllFormats)
         {
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var test = _colour.Cast(fmt);
+            var test = _colour.Cast(fmt);
 
-                var im = test.Zoom(3, 3);
-                Assert.Equal(test.Width * 3, im.Width);
-                Assert.Equal(test.Height * 3, im.Height);
+            var im = test.Zoom(3, 3);
+            Assert.Equal(test.Width * 3, im.Width);
+            Assert.Equal(test.Height * 3, im.Height);
 
-                var before = test[50, 50];
-                var after = im[150, 150];
-                Assert.Equal(before, after);
-            }
+            var before = test[50, 50];
+            var after = im[150, 150];
+            Assert.Equal(before, after);
         }
+    }
 
-        [Fact]
-        public void TestWrap()
+    [Fact]
+    public void TestWrap()
+    {
+        foreach (var fmt in Helper.AllFormats)
         {
-            foreach (var fmt in Helper.AllFormats)
-            {
-                var test = _colour.Cast(fmt);
+            var test = _colour.Cast(fmt);
 
-                var im = test.Wrap();
-                Assert.Equal(test.Width, im.Width);
-                Assert.Equal(test.Height, im.Height);
+            var im = test.Wrap();
+            Assert.Equal(test.Width, im.Width);
+            Assert.Equal(test.Height, im.Height);
 
-                var before = test[0, 0];
-                var after = im[50, 50];
-                Assert.Equal(before, after);
+            var before = test[0, 0];
+            var after = im[50, 50];
+            Assert.Equal(before, after);
 
-                before = test[50, 50];
-                after = im[0, 0];
-                Assert.Equal(before, after);
-            }
+            before = test[50, 50];
+            after = im[0, 0];
+            Assert.Equal(before, after);
         }
     }
 }
\ No newline at end of file
diff --git a/tests/NetVips.Tests/ConvolutionTests.cs b/tests/NetVips.Tests/ConvolutionTests.cs
index 5d535680..79815cbc 100644
--- a/tests/NetVips.Tests/ConvolutionTests.cs
+++ b/tests/NetVips.Tests/ConvolutionTests.cs
@@ -1,309 +1,308 @@
-namespace NetVips.Tests
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace NetVips.Tests;
+
+public class ConvolutionTests : IClassFixture
 {
-    using System;
-    using System.Collections;
-    using System.Collections.Generic;
-    using System.Linq;
-    using Xunit;
-    using Xunit.Abstractions;
-
-    public class ConvolutionTests : IClassFixture
-    {
-        private Image _colour;
-        private Image _mono;
-        private Image[] _allImages;
+    private Image _colour;
+    private Image _mono;
+    private Image[] _allImages;
 
-        private Image _sharp;
-        private Image _blur;
-        private Image _line;
-        private Image _sobel;
-        private Image[] _allMasks;
+    private Image _sharp;
+    private Image _blur;
+    private Image _line;
+    private Image _sobel;
+    private Image[] _allMasks;
 
-        public ConvolutionTests(TestsFixture testsFixture, ITestOutputHelper output)
+    public ConvolutionTests(TestsFixture testsFixture, ITestOutputHelper output)
+    {
+        testsFixture.SetUpLogging(output);
+
+        var im = Image.MaskIdeal(100, 100, 0.5, reject: true, optical: true);
+        _colour = im * new[] { 1, 2, 3 } + new[] { 2, 3, 4 };
+        _colour = _colour.Copy(interpretation: Enums.Interpretation.Srgb);
+        _mono = _colour[0];
+        _mono = _mono.Copy(interpretation: Enums.Interpretation.Bw);
+        _allImages = new[]
         {
-            testsFixture.SetUpLogging(output);
-
-            var im = Image.MaskIdeal(100, 100, 0.5, reject: true, optical: true);
-            _colour = im * new[] { 1, 2, 3 } + new[] { 2, 3, 4 };
-            _colour = _colour.Copy(interpretation: Enums.Interpretation.Srgb);
-            _mono = _colour[0];
-            _mono = _mono.Copy(interpretation: Enums.Interpretation.Bw);
-            _allImages = new[]
-            {
-                _mono,
-                _colour
-            };
-            _sharp = Image.NewFromArray(new[,]
-            {
-                {-1, -1, -1},
-                {-1, 16, -1},
-                {-1, -1, -1}
-            }, 8);
-            _blur = Image.NewFromArray(new[,]
-            {
-                {1, 1, 1},
-                {1, 1, 1},
-                {1, 1, 1}
-            }, 9);
-            _line = Image.NewFromArray(new[,]
-            {
-                {1, 1, 1},
-                {-2, -2, -2},
-                {1, 1, 1}
-            });
-            _sobel = Image.NewFromArray(new[,]
-            {
-                {1, 2, 1},
-                {0, 0, 0},
-                {-1, -2, -1}
-            });
-            _allMasks = new[] { _sharp, _blur, _line, _sobel };
-        }
+            _mono,
+            _colour
+        };
+        _sharp = Image.NewFromArray(new[,]
+        {
+            {-1, -1, -1},
+            {-1, 16, -1},
+            {-1, -1, -1}
+        }, 8);
+        _blur = Image.NewFromArray(new[,]
+        {
+            {1, 1, 1},
+            {1, 1, 1},
+            {1, 1, 1}
+        }, 9);
+        _line = Image.NewFromArray(new[,]
+        {
+            {1, 1, 1},
+            {-2, -2, -2},
+            {1, 1, 1}
+        });
+        _sobel = Image.NewFromArray(new[,]
+        {
+            {1, 2, 1},
+            {0, 0, 0},
+            {-1, -2, -1}
+        });
+        _allMasks = new[] { _sharp, _blur, _line, _sobel };
+    }
 
-        #region helpers
+    #region helpers
 
-        internal object Conv(Image image, Image mask, int xPosition, int yPosition)
+    internal object Conv(Image image, Image mask, int xPosition, int yPosition)
+    {
+        var s = new object[] { 0.0 };
+        for (var x = 0; x < mask.Width; x++)
         {
-            var s = new object[] { 0.0 };
-            for (var x = 0; x < mask.Width; x++)
+            for (var y = 0; y < mask.Height; y++)
             {
-                for (var y = 0; y < mask.Height; y++)
-                {
-                    var m = mask[x, y];
-                    var i = image[x + xPosition, y + yPosition];
-                    var p = Helper.RunFn2((dynamic a, dynamic b) => a * b, m, i);
-                    s = (object[])Helper.RunFn2((dynamic a, dynamic b) => a + b, s, p);
-                }
+                var m = mask[x, y];
+                var i = image[x + xPosition, y + yPosition];
+                var p = Helper.RunFn2((dynamic a, dynamic b) => a * b, m, i);
+                s = (object[])Helper.RunFn2((dynamic a, dynamic b) => a + b, s, p);
             }
-
-            return Helper.RunFn2((dynamic a, dynamic b) => a / b, s, mask.Get("scale"));
         }
 
-        internal object Compass(Image image, Image mask, int xPosition, int yPosition, int nRot,
-            Func func)
-        {
-            var acc = new List();
-            for (var i = 0; i < nRot; i++)
-            {
-                var result = Conv(image, mask, xPosition, yPosition);
-                result = Helper.RunFn((dynamic a) => Math.Abs(a), result);
-                acc.Add(result);
-                mask = mask.Rot45();
-            }
+        return Helper.RunFn2((dynamic a, dynamic b) => a / b, s, mask.Get("scale"));
+    }
 
-            return acc.Aggregate((a, b) => Helper.RunFn2(func, a, b));
+    internal object Compass(Image image, Image mask, int xPosition, int yPosition, int nRot,
+        Func func)
+    {
+        var acc = new List();
+        for (var i = 0; i < nRot; i++)
+        {
+            var result = Conv(image, mask, xPosition, yPosition);
+            result = Helper.RunFn((dynamic a) => Math.Abs(a), result);
+            acc.Add(result);
+            mask = mask.Rot45();
         }
 
-        #endregion
+        return acc.Aggregate((a, b) => Helper.RunFn2(func, a, b));
+    }
+
+    #endregion
 
-        [Fact]
-        public void TestConv()
+    [Fact]
+    public void TestConv()
+    {
+        foreach (var im in _allImages)
         {
-            foreach (var im in _allImages)
+            foreach (var msk in _allMasks)
             {
-                foreach (var msk in _allMasks)
+                foreach (var prec in new[] { Enums.Precision.Integer, Enums.Precision.Float })
                 {
-                    foreach (var prec in new[] { Enums.Precision.Integer, Enums.Precision.Float })
-                    {
-                        var convolved = im.Conv(msk, precision: prec);
+                    var convolved = im.Conv(msk, precision: prec);
 
-                        var result = convolved.Getpoint(25, 50);
-                        var @true = Conv(im, msk, 24, 49) as IEnumerable;
-                        Helper.AssertAlmostEqualObjects(@true, result);
+                    var result = convolved.Getpoint(25, 50);
+                    var @true = Conv(im, msk, 24, 49) as IEnumerable;
+                    Helper.AssertAlmostEqualObjects(@true, result);
 
-                        result = convolved.Getpoint(50, 50);
-                        @true = Conv(im, msk, 49, 49) as IEnumerable;
-                        Helper.AssertAlmostEqualObjects(@true, result);
-                    }
+                    result = convolved.Getpoint(50, 50);
+                    @true = Conv(im, msk, 49, 49) as IEnumerable;
+                    Helper.AssertAlmostEqualObjects(@true, result);
                 }
             }
         }
+    }
 
-        [Fact(Skip = "don't test conva, it's still not done")]
-        public void TestConva()
+    [Fact(Skip = "don't test conva, it's still not done")]
+    public void TestConva()
+    {
+        foreach (var im in _allImages)
         {
-            foreach (var im in _allImages)
+            foreach (var msk in _allMasks)
             {
-                foreach (var msk in _allMasks)
-                {
-                    Console.WriteLine("msk:");
-                    msk.Matrixprint();
-                    Console.WriteLine($"im.bands = {im.Bands}");
+                Console.WriteLine("msk:");
+                msk.Matrixprint();
+                Console.WriteLine($"im.bands = {im.Bands}");
 
-                    var convolved = im.Conv(msk, precision: Enums.Precision.Approximate);
+                var convolved = im.Conv(msk, precision: Enums.Precision.Approximate);
 
-                    var result = convolved.Getpoint(25, 50);
-                    var @true = Conv(im, msk, 24, 49);
-                    Console.WriteLine($"result = {result}, true = {@true}");
-                    Helper.AssertLessThreshold(@true, result, 5);
+                var result = convolved.Getpoint(25, 50);
+                var @true = Conv(im, msk, 24, 49);
+                Console.WriteLine($"result = {result}, true = {@true}");
+                Helper.AssertLessThreshold(@true, result, 5);
 
-                    result = convolved.Getpoint(50, 50);
-                    @true = Conv(im, msk, 49, 49);
-                    Console.WriteLine($"result = {result}, true = {@true}");
-                    Helper.AssertLessThreshold(@true, result, 5);
-                }
+                result = convolved.Getpoint(50, 50);
+                @true = Conv(im, msk, 49, 49);
+                Console.WriteLine($"result = {result}, true = {@true}");
+                Helper.AssertLessThreshold(@true, result, 5);
             }
         }
+    }
 
-        [Fact]
-        public void TestCompass()
+    [Fact]
+    public void TestCompass()
+    {
+        foreach (var im in _allImages)
         {
-            foreach (var im in _allImages)
+            foreach (var msk in _allMasks)
             {
-                foreach (var msk in _allMasks)
+                foreach (var prec in new[] { Enums.Precision.Integer, Enums.Precision.Float })
                 {
-                    foreach (var prec in new[] { Enums.Precision.Integer, Enums.Precision.Float })
+                    for (var times = 1; times < 4; times++)
                     {
-                        for (var times = 1; times < 4; times++)
-                        {
-                            var convolved = im.Compass(msk, times: times, angle: Enums.Angle45.D45,
-                                combine: Enums.Combine.Max, precision: prec);
-
-                            var result = convolved.Getpoint(25, 50);
-                            var @true =
-                                Compass(im, msk, 24, 49, times,
-                                    (dynamic a, dynamic b) => Math.Max(a, b)) as IEnumerable;
-                            Helper.AssertAlmostEqualObjects(@true, result);
-                        }
+                        var convolved = im.Compass(msk, times: times, angle: Enums.Angle45.D45,
+                            combine: Enums.Combine.Max, precision: prec);
+
+                        var result = convolved.Getpoint(25, 50);
+                        var @true =
+                            Compass(im, msk, 24, 49, times,
+                                (dynamic a, dynamic b) => Math.Max(a, b)) as IEnumerable;
+                        Helper.AssertAlmostEqualObjects(@true, result);
                     }
                 }
             }
+        }
 
-            foreach (var im in _allImages)
+        foreach (var im in _allImages)
+        {
+            foreach (var msk in _allMasks)
             {
-                foreach (var msk in _allMasks)
+                foreach (var prec in new[] { Enums.Precision.Integer, Enums.Precision.Float })
                 {
-                    foreach (var prec in new[] { Enums.Precision.Integer, Enums.Precision.Float })
+                    for (var times = 1; times < 4; times++)
                     {
-                        for (var times = 1; times < 4; times++)
-                        {
-                            var convolved = im.Compass(msk, times: times, angle: Enums.Angle45.D45,
-                                combine: Enums.Combine.Sum, precision: prec);
-
-                            var result = convolved.Getpoint(25, 50);
-                            var @true = Compass(im, msk, 24, 49, times, (dynamic a, dynamic b) => a + b) as IEnumerable;
-                            Helper.AssertAlmostEqualObjects(@true, result);
-                        }
+                        var convolved = im.Compass(msk, times: times, angle: Enums.Angle45.D45,
+                            combine: Enums.Combine.Sum, precision: prec);
+
+                        var result = convolved.Getpoint(25, 50);
+                        var @true = Compass(im, msk, 24, 49, times, (dynamic a, dynamic b) => a + b) as IEnumerable;
+                        Helper.AssertAlmostEqualObjects(@true, result);
                     }
                 }
             }
         }
+    }
 
-        [Fact]
-        public void TestConvsep()
+    [Fact]
+    public void TestConvsep()
+    {
+        foreach (var im in _allImages)
         {
-            foreach (var im in _allImages)
+            foreach (var prec in new[] { Enums.Precision.Integer, Enums.Precision.Float })
             {
-                foreach (var prec in new[] { Enums.Precision.Integer, Enums.Precision.Float })
-                {
-                    var gmask = Image.Gaussmat(2, 0.1, precision: prec);
-                    var gmaskSep = Image.Gaussmat(2, 0.1, separable: true, precision: prec);
+                var gmask = Image.Gaussmat(2, 0.1, precision: prec);
+                var gmaskSep = Image.Gaussmat(2, 0.1, separable: true, precision: prec);
 
-                    Assert.Equal(gmask.Width, gmask.Height);
-                    Assert.Equal(gmask.Width, gmaskSep.Width);
-                    Assert.Equal(1, gmaskSep.Height);
+                Assert.Equal(gmask.Width, gmask.Height);
+                Assert.Equal(gmask.Width, gmaskSep.Width);
+                Assert.Equal(1, gmaskSep.Height);
 
-                    var a = im.Conv(gmask, precision: prec);
-                    var b = im.Convsep(gmaskSep, precision: prec);
+                var a = im.Conv(gmask, precision: prec);
+                var b = im.Convsep(gmaskSep, precision: prec);
 
-                    var aPoint = a.Getpoint(25, 50);
-                    var bPoint = b.Getpoint(25, 50);
+                var aPoint = a.Getpoint(25, 50);
+                var bPoint = b.Getpoint(25, 50);
 
-                    Helper.AssertAlmostEqualObjects(aPoint, bPoint, 0.1);
-                }
+                Helper.AssertAlmostEqualObjects(aPoint, bPoint, 0.1);
             }
         }
+    }
 
-        [Fact]
-        public void TestFastcor()
+    [Fact]
+    public void TestFastcor()
+    {
+        foreach (var im in _allImages)
         {
-            foreach (var im in _allImages)
+            foreach (var fmt in Helper.NonComplexFormats)
             {
-                foreach (var fmt in Helper.NonComplexFormats)
-                {
-                    var small = im.ExtractArea(20, 45, 10, 10).Cast(fmt);
-                    var cor = im.Fastcor(small);
-                    var minPos = cor.MinPos();
-                    var v = minPos[0];
-                    var x = minPos[1];
-                    var y = minPos[2];
-
-                    Assert.Equal(0, v);
-                    Assert.Equal(25, x);
-                    Assert.Equal(50, y);
-                }
+                var small = im.ExtractArea(20, 45, 10, 10).Cast(fmt);
+                var cor = im.Fastcor(small);
+                var minPos = cor.MinPos();
+                var v = minPos[0];
+                var x = minPos[1];
+                var y = minPos[2];
+
+                Assert.Equal(0, v);
+                Assert.Equal(25, x);
+                Assert.Equal(50, y);
             }
         }
+    }
 
-        [Fact]
-        public void TestSpcor()
+    [Fact]
+    public void TestSpcor()
+    {
+        foreach (var im in _allImages)
         {
-            foreach (var im in _allImages)
+            foreach (var fmt in Helper.NonComplexFormats)
             {
-                foreach (var fmt in Helper.NonComplexFormats)
-                {
-                    var small = im.ExtractArea(20, 45, 10, 10).Cast(fmt);
-                    var cor = im.Spcor(small);
-                    var maxPos = cor.MaxPos();
-                    var v = maxPos[0];
-                    var x = maxPos[1];
-                    var y = maxPos[2];
-
-                    Assert.Equal(1.0, v);
-                    Assert.Equal(25, x);
-                    Assert.Equal(50, y);
-                }
+                var small = im.ExtractArea(20, 45, 10, 10).Cast(fmt);
+                var cor = im.Spcor(small);
+                var maxPos = cor.MaxPos();
+                var v = maxPos[0];
+                var x = maxPos[1];
+                var y = maxPos[2];
+
+                Assert.Equal(1.0, v);
+                Assert.Equal(25, x);
+                Assert.Equal(50, y);
             }
         }
+    }
 
-        [Fact]
-        public void TestGaussblur()
+    [Fact]
+    public void TestGaussblur()
+    {
+        foreach (var im in _allImages)
         {
-            foreach (var im in _allImages)
+            foreach (var prec in new[] { Enums.Precision.Integer, Enums.Precision.Float })
             {
-                foreach (var prec in new[] { Enums.Precision.Integer, Enums.Precision.Float })
+                for (var i = 5; i < 10; i++)
                 {
-                    for (var i = 5; i < 10; i++)
-                    {
-                        var sigma = i / 5.0;
-                        var gmask = Image.Gaussmat(sigma, 0.2, precision: prec);
+                    var sigma = i / 5.0;
+                    var gmask = Image.Gaussmat(sigma, 0.2, precision: prec);
 
-                        var a = im.Conv(gmask, precision: prec);
-                        var b = im.Gaussblur(sigma, minAmpl: 0.2, precision: prec);
+                    var a = im.Conv(gmask, precision: prec);
+                    var b = im.Gaussblur(sigma, minAmpl: 0.2, precision: prec);
 
-                        var aPoint = a.Getpoint(25, 50);
-                        var bPoint = b.Getpoint(25, 50);
+                    var aPoint = a.Getpoint(25, 50);
+                    var bPoint = b.Getpoint(25, 50);
 
-                        Helper.AssertAlmostEqualObjects(aPoint, bPoint, 0.1);
-                    }
+                    Helper.AssertAlmostEqualObjects(aPoint, bPoint, 0.1);
                 }
             }
         }
+    }
 
-        [Fact]
-        public void TestSharpen()
+    [Fact]
+    public void TestSharpen()
+    {
+        foreach (var im in _allImages)
         {
-            foreach (var im in _allImages)
+            foreach (var fmt in Helper.NonComplexFormats)
             {
-                foreach (var fmt in Helper.NonComplexFormats)
+                foreach (var sigma in new[] { 0.5, 1, 1.5, 2 })
                 {
-                    foreach (var sigma in new[] { 0.5, 1, 1.5, 2 })
-                    {
-                        var im2 = im.Cast(fmt);
-                        var sharp = im2.Sharpen(sigma: sigma);
-
-                        // hard to test much more than this
-                        Assert.Equal(sharp.Width, im.Width);
-                        Assert.Equal(sharp.Height, im.Height);
-
-                        // if m1 and m2 are zero, sharpen should do nothing
-                        sharp = im.Sharpen(sigma: sigma, m1: 0, m2: 0);
-                        sharp = sharp.Colourspace(im.Interpretation);
-                        // Console.WriteLine($"testing sig = {sigma}");
-                        // Console.WriteLine($"testing fmt = {fmt}");
-                        // Console.WriteLine($"max diff = {(im - sharp).Abs().Max()}");
-                        Assert.Equal(0, (im - sharp).Abs().Max());
-                    }
+                    var im2 = im.Cast(fmt);
+                    var sharp = im2.Sharpen(sigma: sigma);
+
+                    // hard to test much more than this
+                    Assert.Equal(sharp.Width, im.Width);
+                    Assert.Equal(sharp.Height, im.Height);
+
+                    // if m1 and m2 are zero, sharpen should do nothing
+                    sharp = im.Sharpen(sigma: sigma, m1: 0, m2: 0);
+                    sharp = sharp.Colourspace(im.Interpretation);
+                    // Console.WriteLine($"testing sig = {sigma}");
+                    // Console.WriteLine($"testing fmt = {fmt}");
+                    // Console.WriteLine($"max diff = {(im - sharp).Abs().Max()}");
+                    Assert.Equal(0, (im - sharp).Abs().Max());
                 }
             }
         }
diff --git a/tests/NetVips.Tests/CreateTests.cs b/tests/NetVips.Tests/CreateTests.cs
index 2723a3c4..fd04d579 100644
--- a/tests/NetVips.Tests/CreateTests.cs
+++ b/tests/NetVips.Tests/CreateTests.cs
@@ -1,567 +1,566 @@
-namespace NetVips.Tests
+using System;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace NetVips.Tests;
+
+public class CreateTests : IClassFixture
 {
-    using System;
-    using Xunit;
-    using Xunit.Abstractions;
+    public CreateTests(TestsFixture testsFixture, ITestOutputHelper output)
+    {
+        testsFixture.SetUpLogging(output);
+    }
 
-    public class CreateTests : IClassFixture
+    [Fact]
+    public void TestBlack()
     {
-        public CreateTests(TestsFixture testsFixture, ITestOutputHelper output)
-        {
-            testsFixture.SetUpLogging(output);
-        }
+        var im = Image.Black(100, 100);
 
-        [Fact]
-        public void TestBlack()
-        {
-            var im = Image.Black(100, 100);
-
-            Assert.Equal(100, im.Width);
-            Assert.Equal(100, im.Height);
-            Assert.Equal(Enums.BandFormat.Uchar, im.Format);
-            Assert.Equal(1, im.Bands);
-
-            for (var i = 0; i < 100; i++)
-            {
-                var pixel = im[i, i];
-                Assert.Single(pixel);
-                Assert.Equal(0, pixel[0]);
-            }
-
-            im = Image.Black(100, 100, bands: 3);
-
-            Assert.Equal(100, im.Width);
-            Assert.Equal(100, im.Height);
-            Assert.Equal(Enums.BandFormat.Uchar, im.Format);
-            Assert.Equal(3, im.Bands);
-
-            for (var i = 0; i < 100; i++)
-            {
-                var pixel = im[i, i];
-                Assert.Equal(3, pixel.Length);
-                Assert.Equal(new double[] { 0, 0, 0 }, pixel);
-            }
-        }
+        Assert.Equal(100, im.Width);
+        Assert.Equal(100, im.Height);
+        Assert.Equal(Enums.BandFormat.Uchar, im.Format);
+        Assert.Equal(1, im.Bands);
 
-        [Fact]
-        public void TestBuildlut()
+        for (var i = 0; i < 100; i++)
         {
-            var m = Image.NewFromArray(new[]
-            {
-                new double[] {0, 0},
-                new double[] {255, 100}
-            });
-            var lut = m.Buildlut();
-            Assert.Equal(256, lut.Width);
-            Assert.Equal(1, lut.Height);
-            Assert.Equal(1, lut.Bands);
-            var p = lut[0, 0];
-            Assert.Equal(0.0, p[0]);
-            p = lut[255, 0];
-            Assert.Equal(100.0, p[0]);
-            p = lut[10, 0];
-            Assert.Equal(100 * 10.0 / 255.0, p[0]);
-
-            m = Image.NewFromArray(new[]
-            {
-                new double[] {0, 0, 100},
-                new double[] {255, 100, 0},
-                new double[] {128, 10, 90}
-            });
-            lut = m.Buildlut();
-            Assert.Equal(256, lut.Width);
-            Assert.Equal(1, lut.Height);
-            Assert.Equal(2, lut.Bands);
-            p = lut[0, 0];
-            Assert.Equal(new[] { 0.0, 100.0 }, p);
-            p = lut[64, 0];
-            Assert.Equal(new[] { 5.0, 95.0 }, p);
+            var pixel = im[i, i];
+            Assert.Single(pixel);
+            Assert.Equal(0, pixel[0]);
         }
 
-        [Fact]
-        public void TestEye()
+        im = Image.Black(100, 100, bands: 3);
+
+        Assert.Equal(100, im.Width);
+        Assert.Equal(100, im.Height);
+        Assert.Equal(Enums.BandFormat.Uchar, im.Format);
+        Assert.Equal(3, im.Bands);
+
+        for (var i = 0; i < 100; i++)
         {
-            var im = Image.Eye(100, 90);
-
-            Assert.Equal(100, im.Width);
-            Assert.Equal(90, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
-            Assert.Equal(1.0, im.Max());
-            Assert.Equal(-1.0, im.Min());
-
-            im = Image.Eye(100, 90, uchar: true);
-            Assert.Equal(100, im.Width);
-            Assert.Equal(90, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Uchar, im.Format);
-            Assert.Equal(255.0, im.Max());
-            Assert.Equal(0.0, im.Min());
+            var pixel = im[i, i];
+            Assert.Equal(3, pixel.Length);
+            Assert.Equal(new double[] { 0, 0, 0 }, pixel);
         }
+    }
 
-        [SkippableFact]
-        public void TestFractsurf()
+    [Fact]
+    public void TestBuildlut()
+    {
+        var m = Image.NewFromArray(new[]
         {
-            Skip.IfNot(Helper.Have("fwfft"), "no FFTW support in this vips, skipping test");
+            new double[] {0, 0},
+            new double[] {255, 100}
+        });
+        var lut = m.Buildlut();
+        Assert.Equal(256, lut.Width);
+        Assert.Equal(1, lut.Height);
+        Assert.Equal(1, lut.Bands);
+        var p = lut[0, 0];
+        Assert.Equal(0.0, p[0]);
+        p = lut[255, 0];
+        Assert.Equal(100.0, p[0]);
+        p = lut[10, 0];
+        Assert.Equal(100 * 10.0 / 255.0, p[0]);
+
+        m = Image.NewFromArray(new[]
+        {
+            new double[] {0, 0, 100},
+            new double[] {255, 100, 0},
+            new double[] {128, 10, 90}
+        });
+        lut = m.Buildlut();
+        Assert.Equal(256, lut.Width);
+        Assert.Equal(1, lut.Height);
+        Assert.Equal(2, lut.Bands);
+        p = lut[0, 0];
+        Assert.Equal(new[] { 0.0, 100.0 }, p);
+        p = lut[64, 0];
+        Assert.Equal(new[] { 5.0, 95.0 }, p);
+    }
 
-            var im = Image.Fractsurf(100, 90, 2.5);
-            Assert.Equal(100, im.Width);
-            Assert.Equal(90, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
-        }
+    [Fact]
+    public void TestEye()
+    {
+        var im = Image.Eye(100, 90);
+
+        Assert.Equal(100, im.Width);
+        Assert.Equal(90, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
+        Assert.Equal(1.0, im.Max());
+        Assert.Equal(-1.0, im.Min());
+
+        im = Image.Eye(100, 90, uchar: true);
+        Assert.Equal(100, im.Width);
+        Assert.Equal(90, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Uchar, im.Format);
+        Assert.Equal(255.0, im.Max());
+        Assert.Equal(0.0, im.Min());
+    }
 
-        [Fact]
-        public void TestGaussmat()
-        {
-            var im = Image.Gaussmat(1, 0.1);
-            Assert.Equal(5, im.Width);
-            Assert.Equal(5, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Double, im.Format);
-            Assert.Equal(20, im.Max());
-            var total = im.Avg() * im.Width * im.Height;
-            var scale = im.Get("scale");
-            Assert.Equal(total, scale);
-            var p = im[im.Width / 2, im.Height / 2];
-            Assert.Equal(20.0, p[0]);
-
-            im = Image.Gaussmat(1, 0.1, separable: true, precision: Enums.Precision.Float);
-            Assert.Equal(5, im.Width);
-            Assert.Equal(1, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Double, im.Format);
-            Assert.Equal(1.0, im.Max());
-            total = im.Avg() * im.Width * im.Height;
-            scale = im.Get("scale");
-            Assert.Equal(total, scale);
-            p = im[im.Width / 2, im.Height / 2];
-            Assert.Equal(1.0, p[0]);
-        }
+    [SkippableFact]
+    public void TestFractsurf()
+    {
+        Skip.IfNot(Helper.Have("fwfft"), "no FFTW support in this vips, skipping test");
 
-        [Fact]
-        public void TestGaussnoise()
-        {
-            var im = Image.Gaussnoise(100, 90);
-            Assert.Equal(100, im.Width);
-            Assert.Equal(90, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
-
-            im = Image.Gaussnoise(100, 90, sigma: 10, mean: 100);
-            Assert.Equal(100, im.Width);
-            Assert.Equal(90, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
-
-            var sigma = im.Deviate();
-            var mean = im.Avg();
-
-            Assert.Equal(10, sigma, 0);
-            Assert.Equal(100, mean, 0);
-        }
+        var im = Image.Fractsurf(100, 90, 2.5);
+        Assert.Equal(100, im.Width);
+        Assert.Equal(90, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
+    }
 
-        [Fact]
-        public void TestGrey()
-        {
-            var im = Image.Grey(100, 90);
-            Assert.Equal(100, im.Width);
-            Assert.Equal(90, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
-
-            var p = im[0, 0];
-            Assert.Equal(0.0, p[0]);
-            p = im[99, 0];
-            Assert.Equal(1.0, p[0]);
-            p = im[0, 89];
-            Assert.Equal(0.0, p[0]);
-            p = im[99, 89];
-            Assert.Equal(1.0, p[0]);
-
-            im = Image.Grey(100, 90, uchar: true);
-            Assert.Equal(100, im.Width);
-            Assert.Equal(90, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Uchar, im.Format);
-
-            p = im[0, 0];
-            Assert.Equal(0, p[0]);
-            p = im[99, 0];
-            Assert.Equal(255, p[0]);
-            p = im[0, 89];
-            Assert.Equal(0, p[0]);
-            p = im[99, 89];
-            Assert.Equal(255, p[0]);
-        }
+    [Fact]
+    public void TestGaussmat()
+    {
+        var im = Image.Gaussmat(1, 0.1);
+        Assert.Equal(5, im.Width);
+        Assert.Equal(5, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Double, im.Format);
+        Assert.Equal(20, im.Max());
+        var total = im.Avg() * im.Width * im.Height;
+        var scale = im.Get("scale");
+        Assert.Equal(total, scale);
+        var p = im[im.Width / 2, im.Height / 2];
+        Assert.Equal(20.0, p[0]);
+
+        im = Image.Gaussmat(1, 0.1, separable: true, precision: Enums.Precision.Float);
+        Assert.Equal(5, im.Width);
+        Assert.Equal(1, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Double, im.Format);
+        Assert.Equal(1.0, im.Max());
+        total = im.Avg() * im.Width * im.Height;
+        scale = im.Get("scale");
+        Assert.Equal(total, scale);
+        p = im[im.Width / 2, im.Height / 2];
+        Assert.Equal(1.0, p[0]);
+    }
 
-        [Fact]
-        public void TestIdentity()
-        {
-            var im = Image.Identity();
-            Assert.Equal(256, im.Width);
-            Assert.Equal(1, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Uchar, im.Format);
-
-            var p = im[0, 0];
-            Assert.Equal(0.0, p[0]);
-            p = im[255, 0];
-            Assert.Equal(255.0, p[0]);
-            p = im[128, 0];
-            Assert.Equal(128.0, p[0]);
-
-            im = Image.Identity(@ushort: true);
-            Assert.Equal(65536, im.Width);
-            Assert.Equal(1, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Ushort, im.Format);
-
-            p = im[0, 0];
-            Assert.Equal(0, p[0]);
-            p = im[99, 0];
-            Assert.Equal(99, p[0]);
-            p = im[65535, 0];
-            Assert.Equal(65535, p[0]);
-        }
+    [Fact]
+    public void TestGaussnoise()
+    {
+        var im = Image.Gaussnoise(100, 90);
+        Assert.Equal(100, im.Width);
+        Assert.Equal(90, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
+
+        im = Image.Gaussnoise(100, 90, sigma: 10, mean: 100);
+        Assert.Equal(100, im.Width);
+        Assert.Equal(90, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
+
+        var sigma = im.Deviate();
+        var mean = im.Avg();
+
+        Assert.Equal(10, sigma, 0);
+        Assert.Equal(100, mean, 0);
+    }
 
-        [Fact]
-        public void TestInvertlut()
-        {
-            var lut = Image.NewFromArray(new[,]
-            {
-                {0.1, 0.2, 0.3, 0.1},
-                {0.2, 0.4, 0.4, 0.2},
-                {0.7, 0.5, 0.6, 0.3}
-            });
-            var im = lut.Invertlut();
-            Assert.Equal(256, im.Width);
-            Assert.Equal(1, im.Height);
-            Assert.Equal(3, im.Bands);
-            Assert.Equal(Enums.BandFormat.Double, im.Format);
-
-            var p = im[0, 0];
-            Assert.Equal(new double[] { 0, 0, 0 }, p);
-            p = im[255, 0];
-            Assert.Equal(new double[] { 1, 1, 1 }, p);
-            p = im[(int)0.2 * 255, 0];
-            Assert.Equal(0, p[0], 1);
-            p = im[(int)0.3 * 255, 0];
-            Assert.Equal(0, p[1], 1);
-            p = im[(int)0.1 * 255, 0];
-            Assert.Equal(0, p[2], 1);
-        }
+    [Fact]
+    public void TestGrey()
+    {
+        var im = Image.Grey(100, 90);
+        Assert.Equal(100, im.Width);
+        Assert.Equal(90, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
+
+        var p = im[0, 0];
+        Assert.Equal(0.0, p[0]);
+        p = im[99, 0];
+        Assert.Equal(1.0, p[0]);
+        p = im[0, 89];
+        Assert.Equal(0.0, p[0]);
+        p = im[99, 89];
+        Assert.Equal(1.0, p[0]);
+
+        im = Image.Grey(100, 90, uchar: true);
+        Assert.Equal(100, im.Width);
+        Assert.Equal(90, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Uchar, im.Format);
+
+        p = im[0, 0];
+        Assert.Equal(0, p[0]);
+        p = im[99, 0];
+        Assert.Equal(255, p[0]);
+        p = im[0, 89];
+        Assert.Equal(0, p[0]);
+        p = im[99, 89];
+        Assert.Equal(255, p[0]);
+    }
 
-        [SkippableFact]
-        public void TestMatrixinvert()
-        {
-            // matrixinvert was added in libvips 8.10.
-            Skip.IfNot(NetVips.AtLeastLibvips(8, 10), "requires libvips >= 8.10");
-
-            // 4x4 matrix to check if PLU decomposition works
-            var mat = Image.NewFromArray(new[,]
-            {
-                {4, 0, 0, 0},
-                {0, 0, 2, 0},
-                {0, 1, 2, 0},
-                {1, 0, 0, 1}
-            });
-            var im = mat.Matrixinvert();
-            Assert.Equal(4, im.Width);
-            Assert.Equal(4, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Double, im.Format);
-
-            var p = im[0, 0];
-            Assert.Equal(0.25, p[0]);
-            p = im[3, 3];
-            Assert.Equal(1.0, p[0]);
-        }
+    [Fact]
+    public void TestIdentity()
+    {
+        var im = Image.Identity();
+        Assert.Equal(256, im.Width);
+        Assert.Equal(1, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Uchar, im.Format);
+
+        var p = im[0, 0];
+        Assert.Equal(0.0, p[0]);
+        p = im[255, 0];
+        Assert.Equal(255.0, p[0]);
+        p = im[128, 0];
+        Assert.Equal(128.0, p[0]);
+
+        im = Image.Identity(@ushort: true);
+        Assert.Equal(65536, im.Width);
+        Assert.Equal(1, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Ushort, im.Format);
+
+        p = im[0, 0];
+        Assert.Equal(0, p[0]);
+        p = im[99, 0];
+        Assert.Equal(99, p[0]);
+        p = im[65535, 0];
+        Assert.Equal(65535, p[0]);
+    }
 
-        [Fact]
-        public void TestLogmat()
+    [Fact]
+    public void TestInvertlut()
+    {
+        var lut = Image.NewFromArray(new[,]
         {
-            var im = Image.Logmat(1, 0.1);
-            Assert.Equal(7, im.Width);
-            Assert.Equal(7, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Double, im.Format);
-            Assert.Equal(20, im.Max());
-
-            var total = im.Avg() * im.Width * im.Height;
-            var scale = (double)im.Get("scale");
-            Assert.Equal(total, scale, 10);
-            var p = im[im.Width / 2, im.Height / 2];
-            Assert.Equal(20.0, p[0]);
-
-            im = Image.Logmat(1, 0.1, separable: true, precision: Enums.Precision.Float);
-            Assert.Equal(7, im.Width);
-            Assert.Equal(1, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Double, im.Format);
-            Assert.Equal(1.0, im.Max());
-            total = im.Avg() * im.Width * im.Height;
-            scale = (double)im.Get("scale");
-            Assert.Equal(total, scale, 10);
-            p = im[im.Width / 2, im.Height / 2];
-            Assert.Equal(1.0, p[0]);
-        }
+            {0.1, 0.2, 0.3, 0.1},
+            {0.2, 0.4, 0.4, 0.2},
+            {0.7, 0.5, 0.6, 0.3}
+        });
+        var im = lut.Invertlut();
+        Assert.Equal(256, im.Width);
+        Assert.Equal(1, im.Height);
+        Assert.Equal(3, im.Bands);
+        Assert.Equal(Enums.BandFormat.Double, im.Format);
+
+        var p = im[0, 0];
+        Assert.Equal(new double[] { 0, 0, 0 }, p);
+        p = im[255, 0];
+        Assert.Equal(new double[] { 1, 1, 1 }, p);
+        p = im[(int)0.2 * 255, 0];
+        Assert.Equal(0, p[0], 1);
+        p = im[(int)0.3 * 255, 0];
+        Assert.Equal(0, p[1], 1);
+        p = im[(int)0.1 * 255, 0];
+        Assert.Equal(0, p[2], 1);
+    }
 
-        [Fact]
-        public void TestMaskButterworthBand()
-        {
-            var im = Image.MaskButterworthBand(128, 128, 2, 0.5, 0.5, 0.7, 0.1);
-            Assert.Equal(128, im.Width);
-            Assert.Equal(128, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
-            Assert.Equal(1, im.Max(), 1);
-            var p = im[32, 32];
-            Assert.Equal(1.0, p[0]);
-
-            im = Image.MaskButterworthBand(128, 128, 2, 0.5, 0.5, 0.7, 0.1, uchar: true, optical: true);
-            Assert.Equal(128, im.Width);
-            Assert.Equal(128, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Uchar, im.Format);
-            Assert.Equal(255, im.Max());
-            p = im[32, 32];
-            Assert.Equal(255.0, p[0]);
-            p = im[64, 64];
-            Assert.Equal(255.0, p[0]);
-
-            im = Image.MaskButterworthBand(128, 128, 2, 0.5, 0.5, 0.7, 0.1, uchar: true, optical: true, nodc: true);
-            Assert.Equal(128, im.Width);
-            Assert.Equal(128, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Uchar, im.Format);
-            Assert.Equal(255, im.Max());
-            p = im[32, 32];
-            Assert.Equal(255.0, p[0]);
-            p = im[64, 64];
-            Assert.NotEqual(255.0, p[0]);
-        }
+    [SkippableFact]
+    public void TestMatrixinvert()
+    {
+        // matrixinvert was added in libvips 8.10.
+        Skip.IfNot(NetVips.AtLeastLibvips(8, 10), "requires libvips >= 8.10");
 
-        [Fact]
-        public void TestMaskButterworth()
+        // 4x4 matrix to check if PLU decomposition works
+        var mat = Image.NewFromArray(new[,]
         {
-            var im = Image.MaskButterworth(128, 128, 2, 0.7, 0.1, nodc: true);
-            Assert.Equal(128, im.Width);
-            Assert.Equal(128, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
-            Assert.Equal(0, im.Min(), 2);
-            var p = im[0, 0];
-            Assert.Equal(0.0, p[0]);
-            var maxPos = im.MaxPos();
-            var x = maxPos[1];
-            var y = maxPos[2];
-            Assert.Equal(64, x);
-            Assert.Equal(64, y);
-
-            im = Image.MaskButterworth(128, 128, 2, 0.7, 0.1, optical: true, uchar: true);
-            Assert.Equal(128, im.Width);
-            Assert.Equal(128, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Uchar, im.Format);
-            Assert.Equal(0, im.Min(), 2);
-            p = im[64, 64];
-            Assert.Equal(255, p[0]);
-        }
+            {4, 0, 0, 0},
+            {0, 0, 2, 0},
+            {0, 1, 2, 0},
+            {1, 0, 0, 1}
+        });
+        var im = mat.Matrixinvert();
+        Assert.Equal(4, im.Width);
+        Assert.Equal(4, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Double, im.Format);
+
+        var p = im[0, 0];
+        Assert.Equal(0.25, p[0]);
+        p = im[3, 3];
+        Assert.Equal(1.0, p[0]);
+    }
 
-        [Fact]
-        public void TestMaskButterworthRing()
-        {
-            var im = Image.MaskButterworthRing(128, 128, 2, 0.7, 0.1, 0.5, nodc: true);
-            Assert.Equal(128, im.Width);
-            Assert.Equal(128, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
-            var p = im[45, 0];
-            Assert.Equal(1.0, p[0], 4);
-
-            var minPos = im.MinPos();
-            var x = minPos[1];
-            var y = minPos[2];
-            Assert.Equal(64, x);
-            Assert.Equal(64, y);
-        }
+    [Fact]
+    public void TestLogmat()
+    {
+        var im = Image.Logmat(1, 0.1);
+        Assert.Equal(7, im.Width);
+        Assert.Equal(7, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Double, im.Format);
+        Assert.Equal(20, im.Max());
+
+        var total = im.Avg() * im.Width * im.Height;
+        var scale = (double)im.Get("scale");
+        Assert.Equal(total, scale, 10);
+        var p = im[im.Width / 2, im.Height / 2];
+        Assert.Equal(20.0, p[0]);
+
+        im = Image.Logmat(1, 0.1, separable: true, precision: Enums.Precision.Float);
+        Assert.Equal(7, im.Width);
+        Assert.Equal(1, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Double, im.Format);
+        Assert.Equal(1.0, im.Max());
+        total = im.Avg() * im.Width * im.Height;
+        scale = (double)im.Get("scale");
+        Assert.Equal(total, scale, 10);
+        p = im[im.Width / 2, im.Height / 2];
+        Assert.Equal(1.0, p[0]);
+    }
 
-        [Fact]
-        public void TestMaskFractal()
-        {
-            var im = Image.MaskFractal(128, 128, 2.3);
-            Assert.Equal(128, im.Width);
-            Assert.Equal(128, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
-        }
+    [Fact]
+    public void TestMaskButterworthBand()
+    {
+        var im = Image.MaskButterworthBand(128, 128, 2, 0.5, 0.5, 0.7, 0.1);
+        Assert.Equal(128, im.Width);
+        Assert.Equal(128, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
+        Assert.Equal(1, im.Max(), 1);
+        var p = im[32, 32];
+        Assert.Equal(1.0, p[0]);
+
+        im = Image.MaskButterworthBand(128, 128, 2, 0.5, 0.5, 0.7, 0.1, uchar: true, optical: true);
+        Assert.Equal(128, im.Width);
+        Assert.Equal(128, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Uchar, im.Format);
+        Assert.Equal(255, im.Max());
+        p = im[32, 32];
+        Assert.Equal(255.0, p[0]);
+        p = im[64, 64];
+        Assert.Equal(255.0, p[0]);
+
+        im = Image.MaskButterworthBand(128, 128, 2, 0.5, 0.5, 0.7, 0.1, uchar: true, optical: true, nodc: true);
+        Assert.Equal(128, im.Width);
+        Assert.Equal(128, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Uchar, im.Format);
+        Assert.Equal(255, im.Max());
+        p = im[32, 32];
+        Assert.Equal(255.0, p[0]);
+        p = im[64, 64];
+        Assert.NotEqual(255.0, p[0]);
+    }
 
-        [Fact]
-        public void TestMaskGaussianBand()
-        {
-            var im = Image.MaskGaussianBand(128, 128, 0.5, 0.5, 0.7, 0.1);
-            Assert.Equal(128, im.Width);
-            Assert.Equal(128, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
-            Assert.Equal(1, im.Max(), 2);
-            var p = im[32, 32];
-            Assert.Equal(1.0, p[0]);
-        }
+    [Fact]
+    public void TestMaskButterworth()
+    {
+        var im = Image.MaskButterworth(128, 128, 2, 0.7, 0.1, nodc: true);
+        Assert.Equal(128, im.Width);
+        Assert.Equal(128, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
+        Assert.Equal(0, im.Min(), 2);
+        var p = im[0, 0];
+        Assert.Equal(0.0, p[0]);
+        var maxPos = im.MaxPos();
+        var x = maxPos[1];
+        var y = maxPos[2];
+        Assert.Equal(64, x);
+        Assert.Equal(64, y);
+
+        im = Image.MaskButterworth(128, 128, 2, 0.7, 0.1, optical: true, uchar: true);
+        Assert.Equal(128, im.Width);
+        Assert.Equal(128, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Uchar, im.Format);
+        Assert.Equal(0, im.Min(), 2);
+        p = im[64, 64];
+        Assert.Equal(255, p[0]);
+    }
 
-        [Fact]
-        public void TestMaskGaussian()
-        {
-            var im = Image.MaskGaussian(128, 128, 0.7, 0.1, nodc: true);
-            Assert.Equal(128, im.Width);
-            Assert.Equal(128, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
-            Assert.Equal(0, im.Min(), 2);
-            var p = im[0, 0];
-            Assert.Equal(0.0, p[0]);
-        }
+    [Fact]
+    public void TestMaskButterworthRing()
+    {
+        var im = Image.MaskButterworthRing(128, 128, 2, 0.7, 0.1, 0.5, nodc: true);
+        Assert.Equal(128, im.Width);
+        Assert.Equal(128, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
+        var p = im[45, 0];
+        Assert.Equal(1.0, p[0], 4);
+
+        var minPos = im.MinPos();
+        var x = minPos[1];
+        var y = minPos[2];
+        Assert.Equal(64, x);
+        Assert.Equal(64, y);
+    }
 
-        [Fact]
-        public void TestMaskGaussianRing()
-        {
-            var im = Image.MaskGaussianRing(128, 128, 0.7, 0.1, 0.5, nodc: true);
-            Assert.Equal(128, im.Width);
-            Assert.Equal(128, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
-            var p = im[45, 0];
-            Assert.Equal(1.0, p[0], 3);
-        }
+    [Fact]
+    public void TestMaskFractal()
+    {
+        var im = Image.MaskFractal(128, 128, 2.3);
+        Assert.Equal(128, im.Width);
+        Assert.Equal(128, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
+    }
 
-        [Fact]
-        public void TestMaskIdealBand()
-        {
-            var im = Image.MaskIdealBand(128, 128, 0.5, 0.5, 0.7);
-            Assert.Equal(128, im.Width);
-            Assert.Equal(128, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
-            Assert.Equal(1, im.Max(), 2);
-            var p = im[32, 32];
-            Assert.Equal(1.0, p[0]);
-        }
+    [Fact]
+    public void TestMaskGaussianBand()
+    {
+        var im = Image.MaskGaussianBand(128, 128, 0.5, 0.5, 0.7, 0.1);
+        Assert.Equal(128, im.Width);
+        Assert.Equal(128, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
+        Assert.Equal(1, im.Max(), 2);
+        var p = im[32, 32];
+        Assert.Equal(1.0, p[0]);
+    }
 
-        [Fact]
-        public void TestMaskIdeal()
-        {
-            var im = Image.MaskIdeal(128, 128, 0.7, nodc: true);
-            Assert.Equal(128, im.Width);
-            Assert.Equal(128, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
-            Assert.Equal(0, im.Min(), 2);
-            var p = im[0, 0];
-            Assert.Equal(0.0, p[0]);
-        }
+    [Fact]
+    public void TestMaskGaussian()
+    {
+        var im = Image.MaskGaussian(128, 128, 0.7, 0.1, nodc: true);
+        Assert.Equal(128, im.Width);
+        Assert.Equal(128, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
+        Assert.Equal(0, im.Min(), 2);
+        var p = im[0, 0];
+        Assert.Equal(0.0, p[0]);
+    }
 
-        [Fact]
-        public void TestMaskGaussianRing2()
-        {
-            var im = Image.MaskIdealRing(128, 128, 0.7, 0.5, nodc: true);
-            Assert.Equal(128, im.Width);
-            Assert.Equal(128, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
-            var p = im[45, 0];
-            Assert.Equal(1.0, p[0], 3);
-        }
+    [Fact]
+    public void TestMaskGaussianRing()
+    {
+        var im = Image.MaskGaussianRing(128, 128, 0.7, 0.1, 0.5, nodc: true);
+        Assert.Equal(128, im.Width);
+        Assert.Equal(128, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
+        var p = im[45, 0];
+        Assert.Equal(1.0, p[0], 3);
+    }
 
-        [Fact]
-        public void TestSines()
-        {
-            var im = Image.Sines(128, 128);
-            Assert.Equal(128, im.Width);
-            Assert.Equal(128, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
-        }
+    [Fact]
+    public void TestMaskIdealBand()
+    {
+        var im = Image.MaskIdealBand(128, 128, 0.5, 0.5, 0.7);
+        Assert.Equal(128, im.Width);
+        Assert.Equal(128, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
+        Assert.Equal(1, im.Max(), 2);
+        var p = im[32, 32];
+        Assert.Equal(1.0, p[0]);
+    }
 
-        [SkippableFact]
-        public void TestText()
-        {
-            Skip.IfNot(Helper.Have("text"), "no text in this vips, skipping test");
-
-            var im = Image.Text("Hello, world!", dpi: 300);
-            Assert.True(im.Width > 10);
-            Assert.True(im.Height > 10);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Uchar, im.Format);
-            Assert.True(im.Max() > 240);
-            Assert.Equal(0, im.Min());
-
-            if (NetVips.AtLeastLibvips(8, 9))
-            {
-                // test autofit
-                im = Image.Text("Hello, world!", width: 500, height: 500);
-
-                // quite a large threshold, since we need to work with a huge range of
-                // text rendering systems
-                Assert.True(Math.Abs(im.Width - 500) < 50);
-            }
-        }
+    [Fact]
+    public void TestMaskIdeal()
+    {
+        var im = Image.MaskIdeal(128, 128, 0.7, nodc: true);
+        Assert.Equal(128, im.Width);
+        Assert.Equal(128, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
+        Assert.Equal(0, im.Min(), 2);
+        var p = im[0, 0];
+        Assert.Equal(0.0, p[0]);
+    }
 
-        [Fact]
-        public void TestTonelut()
-        {
-            var im = Image.Tonelut();
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Ushort, im.Format);
-            Assert.Equal(32768, im.Width);
-            Assert.Equal(1, im.Height);
-            Assert.True(im.HistIsmonotonic());
-        }
+    [Fact]
+    public void TestMaskGaussianRing2()
+    {
+        var im = Image.MaskIdealRing(128, 128, 0.7, 0.5, nodc: true);
+        Assert.Equal(128, im.Width);
+        Assert.Equal(128, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
+        var p = im[45, 0];
+        Assert.Equal(1.0, p[0], 3);
+    }
 
-        [Fact]
-        public void TestXyz()
-        {
-            var im = Image.Xyz(128, 128);
-            Assert.Equal(2, im.Bands);
-            Assert.Equal(Enums.BandFormat.Uint, im.Format);
-            Assert.Equal(128, im.Width);
-            Assert.Equal(128, im.Height);
-            var p = im[45, 35];
-            Assert.Equal(new double[] { 45, 35 }, p);
-        }
+    [Fact]
+    public void TestSines()
+    {
+        var im = Image.Sines(128, 128);
+        Assert.Equal(128, im.Width);
+        Assert.Equal(128, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
+    }
 
-        [Fact]
-        public void TestZone()
-        {
-            var im = Image.Zone(128, 128);
-            Assert.Equal(128, im.Width);
-            Assert.Equal(128, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
-        }
+    [SkippableFact]
+    public void TestText()
+    {
+        Skip.IfNot(Helper.Have("text"), "no text in this vips, skipping test");
+
+        var im = Image.Text("Hello, world!", dpi: 300);
+        Assert.True(im.Width > 10);
+        Assert.True(im.Height > 10);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Uchar, im.Format);
+        Assert.True(im.Max() > 240);
+        Assert.Equal(0, im.Min());
 
-        [SkippableFact]
-        public void TestWorley()
+        if (NetVips.AtLeastLibvips(8, 9))
         {
-            Skip.IfNot(Helper.Have("worley"), "no worley, skipping test");
+            // test autofit
+            im = Image.Text("Hello, world!", width: 500, height: 500);
 
-            var im = Image.Worley(512, 512);
-            Assert.Equal(512, im.Width);
-            Assert.Equal(512, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
+            // quite a large threshold, since we need to work with a huge range of
+            // text rendering systems
+            Assert.True(Math.Abs(im.Width - 500) < 50);
         }
+    }
 
-        [SkippableFact]
-        public void TestPerlin()
-        {
-            Skip.IfNot(Helper.Have("perlin"), "no perlin, skipping test");
+    [Fact]
+    public void TestTonelut()
+    {
+        var im = Image.Tonelut();
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Ushort, im.Format);
+        Assert.Equal(32768, im.Width);
+        Assert.Equal(1, im.Height);
+        Assert.True(im.HistIsmonotonic());
+    }
 
-            var im = Image.Perlin(512, 512);
-            Assert.Equal(512, im.Width);
-            Assert.Equal(512, im.Height);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(Enums.BandFormat.Float, im.Format);
-        }
+    [Fact]
+    public void TestXyz()
+    {
+        var im = Image.Xyz(128, 128);
+        Assert.Equal(2, im.Bands);
+        Assert.Equal(Enums.BandFormat.Uint, im.Format);
+        Assert.Equal(128, im.Width);
+        Assert.Equal(128, im.Height);
+        var p = im[45, 35];
+        Assert.Equal(new double[] { 45, 35 }, p);
+    }
+
+    [Fact]
+    public void TestZone()
+    {
+        var im = Image.Zone(128, 128);
+        Assert.Equal(128, im.Width);
+        Assert.Equal(128, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
+    }
+
+    [SkippableFact]
+    public void TestWorley()
+    {
+        Skip.IfNot(Helper.Have("worley"), "no worley, skipping test");
+
+        var im = Image.Worley(512, 512);
+        Assert.Equal(512, im.Width);
+        Assert.Equal(512, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
+    }
+
+    [SkippableFact]
+    public void TestPerlin()
+    {
+        Skip.IfNot(Helper.Have("perlin"), "no perlin, skipping test");
+
+        var im = Image.Perlin(512, 512);
+        Assert.Equal(512, im.Width);
+        Assert.Equal(512, im.Height);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(Enums.BandFormat.Float, im.Format);
     }
 }
\ No newline at end of file
diff --git a/tests/NetVips.Tests/DrawTests.cs b/tests/NetVips.Tests/DrawTests.cs
index 16dd13d2..bb777f7e 100644
--- a/tests/NetVips.Tests/DrawTests.cs
+++ b/tests/NetVips.Tests/DrawTests.cs
@@ -1,136 +1,135 @@
-namespace NetVips.Tests
+using Xunit;
+using Xunit.Abstractions;
+
+namespace NetVips.Tests;
+
+public class DrawTests : IClassFixture
 {
-    using Xunit;
-    using Xunit.Abstractions;
+    public DrawTests(TestsFixture testsFixture, ITestOutputHelper output)
+    {
+        testsFixture.SetUpLogging(output);
+    }
 
-    public class DrawTests : IClassFixture
+    [Fact]
+    public void TestDrawCircle()
     {
-        public DrawTests(TestsFixture testsFixture, ITestOutputHelper output)
-        {
-            testsFixture.SetUpLogging(output);
-        }
+        var im = Image.Black(100, 100);
+        im = im.Mutate(x => x.DrawCircle(new double[] { 100 }, 50, 50, 25));
+        var pixel = im[25, 50];
+        Assert.Single(pixel);
+        Assert.Equal(100, pixel[0]);
+        pixel = im[26, 50];
+        Assert.Single(pixel);
+        Assert.Equal(0, pixel[0]);
+
+        im = Image.Black(100, 100);
+        im = im.Mutate(x => x.DrawCircle(new double[] { 100 }, 50, 50, 25, fill: true));
+        pixel = im[25, 50];
+        Assert.Single(pixel);
+        Assert.Equal(100, pixel[0]);
+        pixel = im[26, 50];
+        Assert.Equal(100, pixel[0]);
+        pixel = im[24, 50];
+        Assert.Equal(0, pixel[0]);
+    }
 
-        [Fact]
-        public void TestDrawCircle()
-        {
-            var im = Image.Black(100, 100);
-            im = im.Mutate(x => x.DrawCircle(new double[] { 100 }, 50, 50, 25));
-            var pixel = im[25, 50];
-            Assert.Single(pixel);
-            Assert.Equal(100, pixel[0]);
-            pixel = im[26, 50];
-            Assert.Single(pixel);
-            Assert.Equal(0, pixel[0]);
-
-            im = Image.Black(100, 100);
-            im = im.Mutate(x => x.DrawCircle(new double[] { 100 }, 50, 50, 25, fill: true));
-            pixel = im[25, 50];
-            Assert.Single(pixel);
-            Assert.Equal(100, pixel[0]);
-            pixel = im[26, 50];
-            Assert.Equal(100, pixel[0]);
-            pixel = im[24, 50];
-            Assert.Equal(0, pixel[0]);
-        }
-
-        [Fact]
-        public void TestDrawFlood()
+    [Fact]
+    public void TestDrawFlood()
+    {
+        var im = Image.Black(100, 100);
+        im = im.Mutate(x =>
         {
-            var im = Image.Black(100, 100);
-            im = im.Mutate(x =>
-            {
-                x.DrawCircle(new double[] { 100 }, 50, 50, 25);
-                x.DrawFlood(new double[] { 100 }, 50, 50);
-            });
+            x.DrawCircle(new double[] { 100 }, 50, 50, 25);
+            x.DrawFlood(new double[] { 100 }, 50, 50);
+        });
 
-            var im2 = Image.Black(100, 100);
-            im2 = im2.Mutate(x => x.DrawCircle(new double[] { 100 }, 50, 50, 25, fill: true));
+        var im2 = Image.Black(100, 100);
+        im2 = im2.Mutate(x => x.DrawCircle(new double[] { 100 }, 50, 50, 25, fill: true));
 
-            var diff = (im - im2).Abs().Max();
-            Assert.Equal(0, diff);
-        }
+        var diff = (im - im2).Abs().Max();
+        Assert.Equal(0, diff);
+    }
 
-        [Fact]
-        public void TestDrawImage()
-        {
-            var im = Image.Black(51, 51);
-            im = im.Mutate(x => x.DrawCircle(new double[] { 100 }, 25, 25, 25, fill: true));
+    [Fact]
+    public void TestDrawImage()
+    {
+        var im = Image.Black(51, 51);
+        im = im.Mutate(x => x.DrawCircle(new double[] { 100 }, 25, 25, 25, fill: true));
 
-            var im2 = Image.Black(100, 100);
-            im2 = im2.Mutate(x => x.DrawImage(im, 25, 25));
+        var im2 = Image.Black(100, 100);
+        im2 = im2.Mutate(x => x.DrawImage(im, 25, 25));
 
-            var im3 = Image.Black(100, 100);
-            im3 = im3.Mutate(x => x.DrawCircle(new double[] { 100 }, 50, 50, 25, fill: true));
+        var im3 = Image.Black(100, 100);
+        im3 = im3.Mutate(x => x.DrawCircle(new double[] { 100 }, 50, 50, 25, fill: true));
 
-            var diff = (im2 - im3).Abs().Max();
-            Assert.Equal(0, diff);
-        }
+        var diff = (im2 - im3).Abs().Max();
+        Assert.Equal(0, diff);
+    }
 
-        [Fact]
-        public void TestDrawLine()
-        {
-            var im = Image.Black(100, 100);
-            im = im.Mutate(x => x.DrawLine(new double[] { 100 }, 0, 0, 100, 0));
-            var pixel = im[0, 0];
-            Assert.Single(pixel);
-            Assert.Equal(100, pixel[0]);
-            pixel = im[0, 1];
-            Assert.Single(pixel);
-            Assert.Equal(0, pixel[0]);
-        }
-
-        [Fact]
-        public void TestDrawMask()
-        {
-            var mask = Image.Black(51, 51);
-            mask = mask.Mutate(x => x.DrawCircle(new double[] { 128 }, 25, 25, 25, fill: true));
+    [Fact]
+    public void TestDrawLine()
+    {
+        var im = Image.Black(100, 100);
+        im = im.Mutate(x => x.DrawLine(new double[] { 100 }, 0, 0, 100, 0));
+        var pixel = im[0, 0];
+        Assert.Single(pixel);
+        Assert.Equal(100, pixel[0]);
+        pixel = im[0, 1];
+        Assert.Single(pixel);
+        Assert.Equal(0, pixel[0]);
+    }
 
-            var im = Image.Black(100, 100);
-            im = im.Mutate(x => x.DrawMask(new double[] { 200 }, mask, 25, 25));
+    [Fact]
+    public void TestDrawMask()
+    {
+        var mask = Image.Black(51, 51);
+        mask = mask.Mutate(x => x.DrawCircle(new double[] { 128 }, 25, 25, 25, fill: true));
 
-            var im2 = Image.Black(100, 100);
-            im2 = im2.Mutate(x => x.DrawCircle(new double[] { 100 }, 50, 50, 25, fill: true));
+        var im = Image.Black(100, 100);
+        im = im.Mutate(x => x.DrawMask(new double[] { 200 }, mask, 25, 25));
 
-            var diff = (im - im2).Abs().Max();
-            Assert.Equal(0, diff);
-        }
+        var im2 = Image.Black(100, 100);
+        im2 = im2.Mutate(x => x.DrawCircle(new double[] { 100 }, 50, 50, 25, fill: true));
 
-        [Fact]
-        public void TestDrawRect()
-        {
-            var im = Image.Black(100, 100);
-            im = im.Mutate(x => x.DrawRect(new double[] { 100 }, 25, 25, 50, 50, fill: true));
+        var diff = (im - im2).Abs().Max();
+        Assert.Equal(0, diff);
+    }
+
+    [Fact]
+    public void TestDrawRect()
+    {
+        var im = Image.Black(100, 100);
+        im = im.Mutate(x => x.DrawRect(new double[] { 100 }, 25, 25, 50, 50, fill: true));
 
-            var im2 = Image.Black(100, 100);
-            im2 = im2.Mutate(x =>
+        var im2 = Image.Black(100, 100);
+        im2 = im2.Mutate(x =>
+        {
+            for (var y = 25; y < 75; y++)
             {
-                for (var y = 25; y < 75; y++)
-                {
-                    x.DrawLine(new double[] { 100 }, 25, y, 74, y);
-                }
-            });
+                x.DrawLine(new double[] { 100 }, 25, y, 74, y);
+            }
+        });
 
 
-            var diff = (im - im2).Abs().Max();
-            Assert.Equal(0, diff);
-        }
+        var diff = (im - im2).Abs().Max();
+        Assert.Equal(0, diff);
+    }
 
-        [Fact]
-        public void TestDrawSmudge()
-        {
-            var im = Image.Black(100, 100);
-            im = im.Mutate(x => x.DrawCircle(new double[] { 100 }, 50, 50, 25, fill: true));
+    [Fact]
+    public void TestDrawSmudge()
+    {
+        var im = Image.Black(100, 100);
+        im = im.Mutate(x => x.DrawCircle(new double[] { 100 }, 50, 50, 25, fill: true));
 
-            var im2 = im.ExtractArea(10, 10, 50, 50);
+        var im2 = im.ExtractArea(10, 10, 50, 50);
 
-            var im3 = im.Mutate(x =>
-            {
-                x.DrawSmudge(10, 10, 50, 50);
-                x.DrawImage(im2, 10, 10);
-            });
+        var im3 = im.Mutate(x =>
+        {
+            x.DrawSmudge(10, 10, 50, 50);
+            x.DrawImage(im2, 10, 10);
+        });
 
-            var diff = (im3 - im).Abs().Max();
-            Assert.Equal(0, diff);
-        }
+        var diff = (im3 - im).Abs().Max();
+        Assert.Equal(0, diff);
     }
 }
\ No newline at end of file
diff --git a/tests/NetVips.Tests/ExtensionsTests.cs b/tests/NetVips.Tests/ExtensionsTests.cs
index f8821b48..63a06899 100644
--- a/tests/NetVips.Tests/ExtensionsTests.cs
+++ b/tests/NetVips.Tests/ExtensionsTests.cs
@@ -1,94 +1,92 @@
-namespace NetVips.Tests
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+using Xunit;
+using Xunit.Abstractions;
+using NetVips.Extensions;
+
+namespace NetVips.Tests;
+
+[SupportedOSPlatform("windows")]
+public class ExtensionsTests : IClassFixture
 {
-    using Xunit;
-    using Xunit.Abstractions;
-    using System;
-    using System.Drawing;
-    using System.Drawing.Imaging;
-    using System.Runtime.Versioning;
-    using System.Runtime.InteropServices;
-    using Extensions;
-    using Image = Image;
+    public ExtensionsTests(TestsFixture testsFixture, ITestOutputHelper output)
+    {
+        testsFixture.SetUpLogging(output);
+    }
 
-    [SupportedOSPlatform("windows")]
-    public class ExtensionsTests : IClassFixture
+    [Fact]
+    public void ToBitmap1Band()
     {
-        public ExtensionsTests(TestsFixture testsFixture, ITestOutputHelper output)
-        {
-            testsFixture.SetUpLogging(output);
-        }
+        var black = Image.Black(1, 1).Cast(Enums.BandFormat.Uchar);
+        var white = (Image.Black(1, 1) + 255).Cast(Enums.BandFormat.Uchar);
 
-        [Fact]
-        public void ToBitmap1Band()
-        {
-            var black = Image.Black(1, 1).Cast(Enums.BandFormat.Uchar);
-            var white = (Image.Black(1, 1) + 255).Cast(Enums.BandFormat.Uchar);
+        AssertPixelValue(black.WriteToMemory(), black.ToBitmap());
+        AssertPixelValue(white.WriteToMemory(), white.ToBitmap());
+    }
 
-            AssertPixelValue(black.WriteToMemory(), black.ToBitmap());
-            AssertPixelValue(white.WriteToMemory(), white.ToBitmap());
-        }
+    [Fact]
+    public void ToBitmap2Bands()
+    {
+        var black = Image.Black(1, 1, bands: 2).Cast(Enums.BandFormat.Uchar);
+        var white = (Image.Black(1, 1) + new[] { 255, 255 }).Cast(Enums.BandFormat.Uchar);
+        var grey = (Image.Black(1, 1) + new[] { 128, 255 }).Cast(Enums.BandFormat.Uchar);
 
-        [Fact]
-        public void ToBitmap2Bands()
-        {
-            var black = Image.Black(1, 1, bands: 2).Cast(Enums.BandFormat.Uchar);
-            var white = (Image.Black(1, 1) + new[] { 255, 255 }).Cast(Enums.BandFormat.Uchar);
-            var grey = (Image.Black(1, 1) + new[] { 128, 255 }).Cast(Enums.BandFormat.Uchar);
+        AssertPixelValue(black.WriteToMemory(), black.ToBitmap());
+        AssertPixelValue(white.WriteToMemory(), white.ToBitmap());
+        AssertPixelValue(grey.WriteToMemory(), grey.ToBitmap());
+    }
 
-            AssertPixelValue(black.WriteToMemory(), black.ToBitmap());
-            AssertPixelValue(white.WriteToMemory(), white.ToBitmap());
-            AssertPixelValue(grey.WriteToMemory(), grey.ToBitmap());
-        }
+    [Fact]
+    public void ToBitmap3Bands()
+    {
+        var redColor = (Image.Black(1, 1) + new[] { 255, 0, 0 }).Cast(Enums.BandFormat.Uchar);
+        var blueColor = (Image.Black(1, 1) + new[] { 0, 0, 255 }).Cast(Enums.BandFormat.Uchar);
+        var greenColor = (Image.Black(1, 1) + new[] { 0, 255, 0 }).Cast(Enums.BandFormat.Uchar);
 
-        [Fact]
-        public void ToBitmap3Bands()
-        {
-            var redColor = (Image.Black(1, 1) + new[] { 255, 0, 0 }).Cast(Enums.BandFormat.Uchar);
-            var blueColor = (Image.Black(1, 1) + new[] { 0, 0, 255 }).Cast(Enums.BandFormat.Uchar);
-            var greenColor = (Image.Black(1, 1) + new[] { 0, 255, 0 }).Cast(Enums.BandFormat.Uchar);
+        AssertPixelValue(redColor.WriteToMemory(), redColor.ToBitmap());
+        AssertPixelValue(blueColor.WriteToMemory(), blueColor.ToBitmap());
+        AssertPixelValue(greenColor.WriteToMemory(), greenColor.ToBitmap());
+    }
 
-            AssertPixelValue(redColor.WriteToMemory(), redColor.ToBitmap());
-            AssertPixelValue(blueColor.WriteToMemory(), blueColor.ToBitmap());
-            AssertPixelValue(greenColor.WriteToMemory(), greenColor.ToBitmap());
-        }
+    [Fact]
+    public void ToBitmap4Bands()
+    {
+        var redColor = (Image.Black(1, 1) + new[] { 255, 0, 0, 255 }).Cast(Enums.BandFormat.Uchar);
+        var blueColor = (Image.Black(1, 1) + new[] { 0, 0, 255, 255 }).Cast(Enums.BandFormat.Uchar);
+        var greenColor = (Image.Black(1, 1) + new[] { 0, 255, 0, 255 }).Cast(Enums.BandFormat.Uchar);
 
-        [Fact]
-        public void ToBitmap4Bands()
-        {
-            var redColor = (Image.Black(1, 1) + new[] { 255, 0, 0, 255 }).Cast(Enums.BandFormat.Uchar);
-            var blueColor = (Image.Black(1, 1) + new[] { 0, 0, 255, 255 }).Cast(Enums.BandFormat.Uchar);
-            var greenColor = (Image.Black(1, 1) + new[] { 0, 255, 0, 255 }).Cast(Enums.BandFormat.Uchar);
+        AssertPixelValue(redColor.WriteToMemory(), redColor.ToBitmap());
+        AssertPixelValue(blueColor.WriteToMemory(), blueColor.ToBitmap());
+        AssertPixelValue(greenColor.WriteToMemory(), greenColor.ToBitmap());
+    }
 
-            AssertPixelValue(redColor.WriteToMemory(), redColor.ToBitmap());
-            AssertPixelValue(blueColor.WriteToMemory(), blueColor.ToBitmap());
-            AssertPixelValue(greenColor.WriteToMemory(), greenColor.ToBitmap());
-        }
+    private static void AssertPixelValue(byte[] expected, Bitmap actual)
+    {
+        if (actual.Width != 1 || actual.Height != 1)
+            throw new Exception("1x1 image only");
 
-        private static void AssertPixelValue(byte[] expected, Bitmap actual)
+        // An additional band is added for greyscale images
+        if (expected.Length == 2)
         {
-            if (actual.Width != 1 || actual.Height != 1)
-                throw new Exception("1x1 image only");
-
-            // An additional band is added for greyscale images
-            if (expected.Length == 2)
-            {
-                expected = new byte[] { expected[0], expected[1], 255 };
-            }
-
-            var pixels = new byte[expected.Length];
-            var bitmapData = actual.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadOnly, actual.PixelFormat);
-            Marshal.Copy(bitmapData.Scan0, pixels, 0, expected.Length);
-            actual.UnlockBits(bitmapData);
+            expected = new byte[] { expected[0], expected[1], 255 };
+        }
 
-            // Switch from BGR(A) to RGB(A)
-            if (expected.Length > 2)
-            {
-                var t = pixels[0];
-                pixels[0] = pixels[2];
-                pixels[2] = t;
-            }
+        var pixels = new byte[expected.Length];
+        var bitmapData = actual.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadOnly, actual.PixelFormat);
+        Marshal.Copy(bitmapData.Scan0, pixels, 0, expected.Length);
+        actual.UnlockBits(bitmapData);
 
-            Assert.Equal(expected, pixels);
+        // Switch from BGR(A) to RGB(A)
+        if (expected.Length > 2)
+        {
+            var t = pixels[0];
+            pixels[0] = pixels[2];
+            pixels[2] = t;
         }
+
+        Assert.Equal(expected, pixels);
     }
 }
\ No newline at end of file
diff --git a/tests/NetVips.Tests/ForeignTests.cs b/tests/NetVips.Tests/ForeignTests.cs
index a92195c2..0f6d14fb 100644
--- a/tests/NetVips.Tests/ForeignTests.cs
+++ b/tests/NetVips.Tests/ForeignTests.cs
@@ -1,1521 +1,1520 @@
-namespace NetVips.Tests
+using System;
+using System.IO;
+using System.Text;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace NetVips.Tests;
+
+public class ForeignTests : IClassFixture, IDisposable
 {
-    using System;
-    using System.IO;
-    using System.Text;
-    using Xunit;
-    using Xunit.Abstractions;
+    private readonly string _tempDir;
 
-    public class ForeignTests : IClassFixture, IDisposable
-    {
-        private readonly string _tempDir;
+    private Image _colour;
+    private Image _mono;
+    private Image _rad;
+    private Image _cmyk;
+    private Image _oneBit;
 
-        private Image _colour;
-        private Image _mono;
-        private Image _rad;
-        private Image _cmyk;
-        private Image _oneBit;
+    public ForeignTests(TestsFixture testsFixture, ITestOutputHelper output)
+    {
+        testsFixture.SetUpLogging(output);
 
-        public ForeignTests(TestsFixture testsFixture, ITestOutputHelper output)
-        {
-            testsFixture.SetUpLogging(output);
+        _tempDir = Helper.GetTemporaryDirectory();
 
-            _tempDir = Helper.GetTemporaryDirectory();
+        _colour = Image.Jpegload(Helper.JpegFile);
+        _mono = _colour[0];
 
-            _colour = Image.Jpegload(Helper.JpegFile);
-            _mono = _colour[0];
+        // we remove the ICC profile: the RGB one will no longer be appropriate
+        _mono = _mono.Mutate(x => x.Remove("icc-profile-data"));
 
-            // we remove the ICC profile: the RGB one will no longer be appropriate
-            _mono = _mono.Mutate(x => x.Remove("icc-profile-data"));
+        _rad = _colour.Float2rad();
+        _rad = _rad.Mutate(x => x.Remove("icc-profile-data"));
 
-            _rad = _colour.Float2rad();
-            _rad = _rad.Mutate(x => x.Remove("icc-profile-data"));
+        _cmyk = _colour.Bandjoin(_mono);
+        _cmyk = _cmyk.Copy(interpretation: Enums.Interpretation.Cmyk);
+        _cmyk = _cmyk.Mutate(x => x.Remove("icc-profile-data"));
+        var im = Image.NewFromFile(Helper.GifFile);
+        _oneBit = im > 128;
+    }
 
-            _cmyk = _colour.Bandjoin(_mono);
-            _cmyk = _cmyk.Copy(interpretation: Enums.Interpretation.Cmyk);
-            _cmyk = _cmyk.Mutate(x => x.Remove("icc-profile-data"));
-            var im = Image.NewFromFile(Helper.GifFile);
-            _oneBit = im > 128;
+    public void Dispose()
+    {
+        try
+        {
+            Directory.Delete(_tempDir, true);
         }
-
-        public void Dispose()
+        catch (Exception)
         {
-            try
-            {
-                Directory.Delete(_tempDir, true);
-            }
-            catch (Exception)
-            {
-                // ignore
-            }
+            // ignore
         }
+    }
 
-        #region helpers
+    #region helpers
 
-        internal void FileLoader(string loader, string testFile, Action validate)
-        {
-            var im = (Image)Operation.Call(loader, testFile);
-            validate(im);
-            im = Image.NewFromFile(testFile);
-            validate(im);
-        }
+    internal void FileLoader(string loader, string testFile, Action validate)
+    {
+        var im = (Image)Operation.Call(loader, testFile);
+        validate(im);
+        im = Image.NewFromFile(testFile);
+        validate(im);
+    }
 
-        internal void BufferLoader(string loader, string testFile, Action validate)
-        {
-            var buf = File.ReadAllBytes(testFile);
-            var im = (Image)Operation.Call(loader, buf);
-            validate(im);
-            im = Image.NewFromBuffer(buf);
-            validate(im);
-        }
+    internal void BufferLoader(string loader, string testFile, Action validate)
+    {
+        var buf = File.ReadAllBytes(testFile);
+        var im = (Image)Operation.Call(loader, buf);
+        validate(im);
+        im = Image.NewFromBuffer(buf);
+        validate(im);
+    }
 
-        internal void SaveLoad(string format, Image im)
-        {
-            var x = Image.NewTempFile(format);
-            im.Write(x);
+    internal void SaveLoad(string format, Image im)
+    {
+        var x = Image.NewTempFile(format);
+        im.Write(x);
+
+        Assert.Equal(im.Width, x.Width);
+        Assert.Equal(im.Height, x.Height);
+        Assert.Equal(im.Bands, x.Bands);
+        var maxDiff = (im - x).Abs().Max();
+        Assert.Equal(0, maxDiff);
+    }
 
-            Assert.Equal(im.Width, x.Width);
-            Assert.Equal(im.Height, x.Height);
-            Assert.Equal(im.Bands, x.Bands);
-            var maxDiff = (im - x).Abs().Max();
-            Assert.Equal(0, maxDiff);
-        }
+    internal void SaveLoadFile(string format, string options, Image im, int maxDiff = 0)
+    {
+        // yuk!
+        // but we can't set format parameters for Image.NewTempFile()
+        var filename = Helper.GetTemporaryFile(_tempDir, format);
 
-        internal void SaveLoadFile(string format, string options, Image im, int maxDiff = 0)
-        {
-            // yuk!
-            // but we can't set format parameters for Image.NewTempFile()
-            var filename = Helper.GetTemporaryFile(_tempDir, format);
+        im.WriteToFile(filename + options);
+        var x = Image.NewFromFile(filename);
+
+        Assert.Equal(im.Width, x.Width);
+        Assert.Equal(im.Height, x.Height);
+        Assert.Equal(im.Bands, x.Bands);
+        Assert.True((im - x).Abs().Max() <= maxDiff);
+    }
 
-            im.WriteToFile(filename + options);
-            var x = Image.NewFromFile(filename);
+    internal void SaveLoadBuffer(string saver, string loader, Image im, int maxDiff = 0, VOption kwargs = null)
+    {
+        var buf = (byte[])Operation.Call(saver, kwargs, im);
+        var x = (Image)Operation.Call(loader, buf);
 
-            Assert.Equal(im.Width, x.Width);
-            Assert.Equal(im.Height, x.Height);
-            Assert.Equal(im.Bands, x.Bands);
-            Assert.True((im - x).Abs().Max() <= maxDiff);
-        }
+        Assert.Equal(im.Width, x.Width);
+        Assert.Equal(im.Height, x.Height);
+        Assert.Equal(im.Bands, x.Bands);
+        Assert.True((im - x).Abs().Max() <= maxDiff);
+    }
 
-        internal void SaveLoadBuffer(string saver, string loader, Image im, int maxDiff = 0, VOption kwargs = null)
-        {
-            var buf = (byte[])Operation.Call(saver, kwargs, im);
-            var x = (Image)Operation.Call(loader, buf);
+    internal void SaveLoadStream(string format, string options, Image im, int maxDiff = 0)
+    {
+        using var stream = new MemoryStream();
+        im.WriteToStream(stream, format + options);
 
-            Assert.Equal(im.Width, x.Width);
-            Assert.Equal(im.Height, x.Height);
-            Assert.Equal(im.Bands, x.Bands);
-            Assert.True((im - x).Abs().Max() <= maxDiff);
-        }
+        // Reset to start position
+        stream.Seek(0, SeekOrigin.Begin);
 
-        internal void SaveLoadStream(string format, string options, Image im, int maxDiff = 0)
-        {
-            using var stream = new MemoryStream();
-            im.WriteToStream(stream, format + options);
+        var x = Image.NewFromStream(stream);
 
-            // Reset to start position
-            stream.Seek(0, SeekOrigin.Begin);
+        Assert.Equal(im.Width, x.Width);
+        Assert.Equal(im.Height, x.Height);
+        Assert.Equal(im.Bands, x.Bands);
+        Assert.True((im - x).Abs().Max() <= maxDiff);
+    }
 
-            var x = Image.NewFromStream(stream);
+    internal void SaveBufferTempFile(string saver, string suf, Image im, int maxDiff = 0)
+    {
+        var filename = Helper.GetTemporaryFile(_tempDir, suf);
 
-            Assert.Equal(im.Width, x.Width);
-            Assert.Equal(im.Height, x.Height);
-            Assert.Equal(im.Bands, x.Bands);
-            Assert.True((im - x).Abs().Max() <= maxDiff);
-        }
+        var buf = (byte[])Operation.Call(saver, matchImage: im);
+        File.WriteAllBytes(filename, buf);
 
-        internal void SaveBufferTempFile(string saver, string suf, Image im, int maxDiff = 0)
-        {
-            var filename = Helper.GetTemporaryFile(_tempDir, suf);
+        var x = Image.NewFromFile(filename);
 
-            var buf = (byte[])Operation.Call(saver, matchImage: im);
-            File.WriteAllBytes(filename, buf);
+        Assert.Equal(im.Width, x.Width);
+        Assert.Equal(im.Height, x.Height);
+        Assert.Equal(im.Bands, x.Bands);
+        Assert.True((im - x).Abs().Max() <= maxDiff);
+    }
 
-            var x = Image.NewFromFile(filename);
+    #endregion
 
-            Assert.Equal(im.Width, x.Width);
-            Assert.Equal(im.Height, x.Height);
-            Assert.Equal(im.Bands, x.Bands);
-            Assert.True((im - x).Abs().Max() <= maxDiff);
-        }
+    [Fact]
+    public void TestVips()
+    {
+        SaveLoadFile(".v", "", _colour);
 
-        #endregion
+        // check we can save and restore metadata
+        var filename = Helper.GetTemporaryFile(_tempDir, ".v");
+        _colour.WriteToFile(filename);
+        var x = Image.NewFromFile(filename);
+        var beforeExif = (byte[])_colour.Get("exif-data");
+        var afterExif = (byte[])x.Get("exif-data");
 
-        [Fact]
-        public void TestVips()
+        Assert.Equal(beforeExif.Length, afterExif.Length);
+        Assert.Equal(beforeExif, afterExif);
+    }
+
+    [SkippableFact]
+    public void TestJpeg()
+    {
+        Skip.IfNot(Helper.Have("jpegload"), "no jpeg support in this vips, skipping test");
+
+        void JpegValid(Image im)
         {
-            SaveLoadFile(".v", "", _colour);
+            var a = im[10, 10];
+            Assert.Equal(new double[] { 6, 5, 3 }, a);
+            var profile = (byte[])im.Get("icc-profile-data");
+
+            Assert.Equal(1352, profile.Length);
+            Assert.Equal(1024, im.Width);
+            Assert.Equal(768, im.Height);
+            Assert.Equal(3, im.Bands);
+        }
 
-            // check we can save and restore metadata
-            var filename = Helper.GetTemporaryFile(_tempDir, ".v");
-            _colour.WriteToFile(filename);
-            var x = Image.NewFromFile(filename);
-            var beforeExif = (byte[])_colour.Get("exif-data");
-            var afterExif = (byte[])x.Get("exif-data");
+        FileLoader("jpegload", Helper.JpegFile, JpegValid);
+        SaveLoad("%s.jpg", _mono);
+        SaveLoad("%s.jpg", _colour);
 
-            Assert.Equal(beforeExif.Length, afterExif.Length);
-            Assert.Equal(beforeExif, afterExif);
+        BufferLoader("jpegload_buffer", Helper.JpegFile, JpegValid);
+        SaveLoadBuffer("jpegsave_buffer", "jpegload_buffer", _colour, 80);
+        if (Helper.Have("jpegload_source"))
+        {
+            SaveLoadStream(".jpg", "", _colour, 80);
         }
 
-        [SkippableFact]
-        public void TestJpeg()
+        _ = Image.Jpegload(Helper.JpegFile, out var flags);
+        Assert.Equal(Enums.ForeignFlags.SEQUENTIAL, flags);
+
+        // see if we have exif parsing: our test image has this field
+        var x = Image.NewFromFile(Helper.JpegFile);
+        if (x.Contains("exif-ifd0-Orientation"))
         {
-            Skip.IfNot(Helper.Have("jpegload"), "no jpeg support in this vips, skipping test");
+            // we need a copy of the image to set the new metadata on
+            // otherwise we get caching problems
 
-            void JpegValid(Image im)
-            {
-                var a = im[10, 10];
-                Assert.Equal(new double[] { 6, 5, 3 }, a);
-                var profile = (byte[])im.Get("icc-profile-data");
-
-                Assert.Equal(1352, profile.Length);
-                Assert.Equal(1024, im.Width);
-                Assert.Equal(768, im.Height);
-                Assert.Equal(3, im.Bands);
-            }
+            // can set, save and load new orientation
+            x = Image.NewFromFile(Helper.JpegFile);
+            x = x.Mutate(im => im.Set("orientation", 2));
 
-            FileLoader("jpegload", Helper.JpegFile, JpegValid);
-            SaveLoad("%s.jpg", _mono);
-            SaveLoad("%s.jpg", _colour);
+            var filename = Helper.GetTemporaryFile(_tempDir, ".jpg");
+            x.WriteToFile(filename);
 
-            BufferLoader("jpegload_buffer", Helper.JpegFile, JpegValid);
-            SaveLoadBuffer("jpegsave_buffer", "jpegload_buffer", _colour, 80);
-            if (Helper.Have("jpegload_source"))
-            {
-                SaveLoadStream(".jpg", "", _colour, 80);
-            }
+            x = Image.NewFromFile(filename);
+            var y = x.Get("orientation");
+            Assert.Equal(2, y);
+
+            // can remove orientation, save, load again, orientation
+            // has reset
+            x = x.Mutate(im => im.Remove("orientation"));
 
-            _ = Image.Jpegload(Helper.JpegFile, out var flags);
-            Assert.Equal(Enums.ForeignFlags.SEQUENTIAL, flags);
+            filename = Helper.GetTemporaryFile(_tempDir, ".jpg");
+            x.WriteToFile(filename);
 
-            // see if we have exif parsing: our test image has this field
-            var x = Image.NewFromFile(Helper.JpegFile);
-            if (x.Contains("exif-ifd0-Orientation"))
+            x = Image.NewFromFile(filename);
+            y = x.Get("orientation");
+            Assert.Equal(1, y);
+
+            // autorotate load works
+            x = Image.NewFromFile(Helper.JpegFile);
+            x = x.Mutate(im => im.Set("orientation", 6));
+
+            filename = Helper.GetTemporaryFile(_tempDir, ".jpg");
+            x.WriteToFile(filename);
+            var x1 = Image.NewFromFile(filename);
+            var x2 = Image.NewFromFile(filename, kwargs: new VOption
             {
-                // we need a copy of the image to set the new metadata on
-                // otherwise we get caching problems
+                {"autorotate", true}
+            });
+            Assert.Equal(x1.Width, x2.Height);
+            Assert.Equal(x1.Height, x2.Width);
 
-                // can set, save and load new orientation
+            // can set, save and reload ASCII string fields
+            // added in 8.7
+            if (NetVips.AtLeastLibvips(8, 7))
+            {
                 x = Image.NewFromFile(Helper.JpegFile);
-                x = x.Mutate(im => im.Set("orientation", 2));
+                x = x.Mutate(im => im.Set(GValue.GStrType, "exif-ifd0-ImageDescription", "hello world"));
 
-                var filename = Helper.GetTemporaryFile(_tempDir, ".jpg");
+                filename = Helper.GetTemporaryFile(_tempDir, ".jpg");
                 x.WriteToFile(filename);
 
                 x = Image.NewFromFile(filename);
-                var y = x.Get("orientation");
-                Assert.Equal(2, y);
+                y = x.Get("exif-ifd0-ImageDescription");
 
-                // can remove orientation, save, load again, orientation
-                // has reset
-                x = x.Mutate(im => im.Remove("orientation"));
+                // can't use Assert.Equal since the string will have an extra " (xx, yy, zz)"
+                // format area at the end
+                Assert.StartsWith("hello world", (string)y);
+
+                // can set, save and reload UTF16 string fields ... NetVips is
+                // utf8, but it will be coded as utf16 and back for the XP* fields
+                x = Image.NewFromFile(Helper.JpegFile);
+                x = x.Mutate(im => im.Set(GValue.GStrType, "exif-ifd0-XPComment", "йцук"));
 
                 filename = Helper.GetTemporaryFile(_tempDir, ".jpg");
                 x.WriteToFile(filename);
 
                 x = Image.NewFromFile(filename);
-                y = x.Get("orientation");
-                Assert.Equal(1, y);
+                y = x.Get("exif-ifd0-XPComment");
+
+                // can't use Assert.Equal since the string will have an extra " (xx, yy, zz)"
+                // format area at the end
+                Assert.StartsWith("йцук", (string)y);
 
-                // autorotate load works
+                // can set/save/load UserComment, a tag which has the
+                // encoding in the first 8 bytes ... though libexif only supports
+                // ASCII for this
                 x = Image.NewFromFile(Helper.JpegFile);
-                x = x.Mutate(im => im.Set("orientation", 6));
+                x = x.Mutate(im => im.Set(GValue.GStrType, "exif-ifd2-UserComment", "hello world"));
 
                 filename = Helper.GetTemporaryFile(_tempDir, ".jpg");
                 x.WriteToFile(filename);
-                var x1 = Image.NewFromFile(filename);
-                var x2 = Image.NewFromFile(filename, kwargs: new VOption
-                {
-                    {"autorotate", true}
-                });
-                Assert.Equal(x1.Width, x2.Height);
-                Assert.Equal(x1.Height, x2.Width);
-
-                // can set, save and reload ASCII string fields
-                // added in 8.7
-                if (NetVips.AtLeastLibvips(8, 7))
-                {
-                    x = Image.NewFromFile(Helper.JpegFile);
-                    x = x.Mutate(im => im.Set(GValue.GStrType, "exif-ifd0-ImageDescription", "hello world"));
 
-                    filename = Helper.GetTemporaryFile(_tempDir, ".jpg");
-                    x.WriteToFile(filename);
+                x = Image.NewFromFile(filename);
+                y = x.Get("exif-ifd2-UserComment");
 
-                    x = Image.NewFromFile(filename);
-                    y = x.Get("exif-ifd0-ImageDescription");
+                // can't use Assert.Equal since the string will have an extra " (xx, yy, zz)"
+                // format area at the end
+                Assert.StartsWith("hello world", (string)y);
+            }
+        }
+    }
 
-                    // can't use Assert.Equal since the string will have an extra " (xx, yy, zz)"
-                    // format area at the end
-                    Assert.StartsWith("hello world", (string)y);
+    [SkippableFact]
+    public void TestJpegSave()
+    {
+        Skip.IfNot(Helper.Have("jpegsave") && NetVips.AtLeastLibvips(8, 10),
+            "requires libvips >= 8.10 with jpeg save support");
 
-                    // can set, save and reload UTF16 string fields ... NetVips is
-                    // utf8, but it will be coded as utf16 and back for the XP* fields
-                    x = Image.NewFromFile(Helper.JpegFile);
-                    x = x.Mutate(im => im.Set(GValue.GStrType, "exif-ifd0-XPComment", "йцук"));
+        var im = Image.NewFromFile(Helper.JpegFile);
 
-                    filename = Helper.GetTemporaryFile(_tempDir, ".jpg");
-                    x.WriteToFile(filename);
+        var q10 = im.JpegsaveBuffer(q: 10);
+        var q10SubsampleAuto = im.JpegsaveBuffer(q: 10, subsampleMode: Enums.ForeignSubsample.Auto);
+        var q10SubsampleOn = im.JpegsaveBuffer(q: 10, subsampleMode: Enums.ForeignSubsample.On);
+        var q10SubsampleOff = im.JpegsaveBuffer(q: 10, subsampleMode: Enums.ForeignSubsample.Off);
 
-                    x = Image.NewFromFile(filename);
-                    y = x.Get("exif-ifd0-XPComment");
+        var q90 = im.JpegsaveBuffer(q: 90);
+        var q90SubsampleAuto = im.JpegsaveBuffer(q: 90, subsampleMode: Enums.ForeignSubsample.Auto);
+        var q90SubsampleOn = im.JpegsaveBuffer(q: 90, subsampleMode: Enums.ForeignSubsample.On);
+        var q90SubsampleOff = im.JpegsaveBuffer(q: 90, subsampleMode: Enums.ForeignSubsample.Off);
 
-                    // can't use Assert.Equal since the string will have an extra " (xx, yy, zz)"
-                    // format area at the end
-                    Assert.StartsWith("йцук", (string)y);
+        // higher Q should mean a bigger buffer
+        Assert.True(q90.Length > q10.Length);
 
-                    // can set/save/load UserComment, a tag which has the
-                    // encoding in the first 8 bytes ... though libexif only supports
-                    // ASCII for this
-                    x = Image.NewFromFile(Helper.JpegFile);
-                    x = x.Mutate(im => im.Set(GValue.GStrType, "exif-ifd2-UserComment", "hello world"));
+        Assert.Equal(q10.Length, q10SubsampleAuto.Length);
+        Assert.Equal(q10SubsampleAuto.Length, q10SubsampleOn.Length);
+        Assert.True(q10SubsampleOff.Length > q10.Length);
 
-                    filename = Helper.GetTemporaryFile(_tempDir, ".jpg");
-                    x.WriteToFile(filename);
+        Assert.Equal(q90SubsampleAuto.Length, q90.Length);
+        Assert.True(q90SubsampleOn.Length < q90.Length);
+        Assert.Equal(q90SubsampleAuto.Length, q90SubsampleOff.Length);
+    }
 
-                    x = Image.NewFromFile(filename);
-                    y = x.Get("exif-ifd2-UserComment");
+    [SkippableFact]
+    public void TestTruncated()
+    {
+        Skip.IfNot(Helper.Have("jpegload"), "no jpeg support in this vips, skipping test");
 
-                    // can't use Assert.Equal since the string will have an extra " (xx, yy, zz)"
-                    // format area at the end
-                    Assert.StartsWith("hello world", (string)y);
-                }
-            }
-        }
+        // This should open (there's enough there for the header)
+        var im = Image.NewFromFile(Helper.TruncatedFile);
 
-        [SkippableFact]
-        public void TestJpegSave()
-        {
-            Skip.IfNot(Helper.Have("jpegsave") && NetVips.AtLeastLibvips(8, 10),
-                "requires libvips >= 8.10 with jpeg save support");
+        // but this should fail with a warning, and knock TRUNCATED_FILE out of
+        // the cache
+        _ = im.Avg();
 
-            var im = Image.NewFromFile(Helper.JpegFile);
+        // now we should open again, but it won't come from cache, it'll reload
+        im = Image.NewFromFile(Helper.TruncatedFile);
 
-            var q10 = im.JpegsaveBuffer(q: 10);
-            var q10SubsampleAuto = im.JpegsaveBuffer(q: 10, subsampleMode: Enums.ForeignSubsample.Auto);
-            var q10SubsampleOn = im.JpegsaveBuffer(q: 10, subsampleMode: Enums.ForeignSubsample.On);
-            var q10SubsampleOff = im.JpegsaveBuffer(q: 10, subsampleMode: Enums.ForeignSubsample.Off);
+        // and this should fail with a warning once more
+        _ = im.Avg();
+    }
 
-            var q90 = im.JpegsaveBuffer(q: 90);
-            var q90SubsampleAuto = im.JpegsaveBuffer(q: 90, subsampleMode: Enums.ForeignSubsample.Auto);
-            var q90SubsampleOn = im.JpegsaveBuffer(q: 90, subsampleMode: Enums.ForeignSubsample.On);
-            var q90SubsampleOff = im.JpegsaveBuffer(q: 90, subsampleMode: Enums.ForeignSubsample.Off);
+    [SkippableFact]
+    public void TestPng()
+    {
+        Skip.IfNot(Helper.Have("pngload") && File.Exists(Helper.PngFile), "no png support, skipping test");
 
-            // higher Q should mean a bigger buffer
-            Assert.True(q90.Length > q10.Length);
+        void PngValid(Image im)
+        {
+            var a = im[10, 10];
 
-            Assert.Equal(q10.Length, q10SubsampleAuto.Length);
-            Assert.Equal(q10SubsampleAuto.Length, q10SubsampleOn.Length);
-            Assert.True(q10SubsampleOff.Length > q10.Length);
+            Assert.Equal(new[] { 38671.0, 33914.0, 26762.0 }, a);
+            Assert.Equal(290, im.Width);
+            Assert.Equal(442, im.Height);
+            Assert.Equal(3, im.Bands);
+        }
 
-            Assert.Equal(q90SubsampleAuto.Length, q90.Length);
-            Assert.True(q90SubsampleOn.Length < q90.Length);
-            Assert.Equal(q90SubsampleAuto.Length, q90SubsampleOff.Length);
+        FileLoader("pngload", Helper.PngFile, PngValid);
+        BufferLoader("pngload_buffer", Helper.PngFile, PngValid);
+        SaveLoadBuffer("pngsave_buffer", "pngload_buffer", _colour);
+        SaveLoad("%s.png", _mono);
+        SaveLoad("%s.png", _colour);
+        SaveLoadFile(".png", "[interlace]", _colour);
+        SaveLoadFile(".png", "[interlace]", _mono);
+
+        if (Helper.Have("pngload_source"))
+        {
+            SaveLoadStream(".png", "", _colour);
         }
 
-        [SkippableFact]
-        public void TestTruncated()
+        // bitdepth option was added in libvips 8.10
+        if (NetVips.AtLeastLibvips(8, 10))
         {
-            Skip.IfNot(Helper.Have("jpegload"), "no jpeg support in this vips, skipping test");
+            // size of a regular mono PNG
+            var lenMono = _mono.PngsaveBuffer().Length;
 
-            // This should open (there's enough there for the header)
-            var im = Image.NewFromFile(Helper.TruncatedFile);
+            // 4-bit should be smaller
+            var lenMono4 = _mono.PngsaveBuffer(bitdepth: 4).Length;
+            Assert.True(lenMono4 < lenMono);
 
-            // but this should fail with a warning, and knock TRUNCATED_FILE out of
-            // the cache
-            _ = im.Avg();
+            var lenMono2 = _mono.PngsaveBuffer(bitdepth: 2).Length;
+            Assert.True(lenMono2 < lenMono4);
 
-            // now we should open again, but it won't come from cache, it'll reload
-            im = Image.NewFromFile(Helper.TruncatedFile);
+            var lenMono1 = _mono.PngsaveBuffer(bitdepth: 1).Length;
+            Assert.True(lenMono1 < lenMono2);
 
-            // and this should fail with a warning once more
-            _ = im.Avg();
+            // we can't test palette save since we can't be sure libimagequant is
+            // available and there's no easy test for its presence
         }
+    }
 
-        [SkippableFact]
-        public void TestPng()
-        {
-            Skip.IfNot(Helper.Have("pngload") && File.Exists(Helper.PngFile), "no png support, skipping test");
+    [SkippableFact]
+    public void TestBufferOverload()
+    {
+        Skip.IfNot(Helper.Have("pngload"), "no png support, skipping test");
 
-            void PngValid(Image im)
-            {
-                var a = im[10, 10];
+        var buf = _colour.WriteToBuffer(".png");
+        var x = Image.NewFromBuffer(buf);
 
-                Assert.Equal(new[] { 38671.0, 33914.0, 26762.0 }, a);
-                Assert.Equal(290, im.Width);
-                Assert.Equal(442, im.Height);
-                Assert.Equal(3, im.Bands);
-            }
+        Assert.Equal(_colour.Width, x.Width);
+        Assert.Equal(_colour.Height, x.Height);
+        Assert.Equal(_colour.Bands, x.Bands);
+        Assert.Equal(0, (_colour - x).Abs().Max());
+    }
 
-            FileLoader("pngload", Helper.PngFile, PngValid);
-            BufferLoader("pngload_buffer", Helper.PngFile, PngValid);
-            SaveLoadBuffer("pngsave_buffer", "pngload_buffer", _colour);
-            SaveLoad("%s.png", _mono);
-            SaveLoad("%s.png", _colour);
-            SaveLoadFile(".png", "[interlace]", _colour);
-            SaveLoadFile(".png", "[interlace]", _mono);
+    [SkippableFact]
+    public void TestStreamOverload()
+    {
+        Skip.IfNot(Helper.Have("jpegload_source"), "no jpeg source support, skipping test");
 
-            if (Helper.Have("pngload_source"))
-            {
-                SaveLoadStream(".png", "", _colour);
-            }
+        // Set the beginning of the stream to an arbitrary but carefully chosen number.
+        using var stream = new MemoryStream { Position = 42 };
+        _colour.WriteToStream(stream, ".jpg");
 
-            // bitdepth option was added in libvips 8.10
-            if (NetVips.AtLeastLibvips(8, 10))
-            {
-                // size of a regular mono PNG
-                var lenMono = _mono.PngsaveBuffer().Length;
+        // Set the current position of the stream to the chosen number.
+        stream.Position = 42;
 
-                // 4-bit should be smaller
-                var lenMono4 = _mono.PngsaveBuffer(bitdepth: 4).Length;
-                Assert.True(lenMono4 < lenMono);
+        // We should be able to read from this stream, even if it starts at any position.
+        var x = Image.NewFromStream(stream, access: Enums.Access.Sequential);
 
-                var lenMono2 = _mono.PngsaveBuffer(bitdepth: 2).Length;
-                Assert.True(lenMono2 < lenMono4);
+        Assert.Equal(_colour.Width, x.Width);
+        Assert.Equal(_colour.Height, x.Height);
+        Assert.Equal(_colour.Bands, x.Bands);
+        Assert.True((_colour - x).Abs().Max() <= 80);
+    }
 
-                var lenMono1 = _mono.PngsaveBuffer(bitdepth: 1).Length;
-                Assert.True(lenMono1 < lenMono2);
+    [SkippableFact]
+    public void TestTiff()
+    {
+        Skip.IfNot(Helper.Have("tiffload") && File.Exists(Helper.TifFile), "no tiff support, skipping test");
 
-                // we can't test palette save since we can't be sure libimagequant is
-                // available and there's no easy test for its presence
-            }
-        }
+        var vips810 = NetVips.AtLeastLibvips(8, 10);
 
-        [SkippableFact]
-        public void TestBufferOverload()
+        void TiffValid(Image im)
         {
-            Skip.IfNot(Helper.Have("pngload"), "no png support, skipping test");
-
-            var buf = _colour.WriteToBuffer(".png");
-            var x = Image.NewFromBuffer(buf);
+            var a = im[10, 10];
 
-            Assert.Equal(_colour.Width, x.Width);
-            Assert.Equal(_colour.Height, x.Height);
-            Assert.Equal(_colour.Bands, x.Bands);
-            Assert.Equal(0, (_colour - x).Abs().Max());
+            Assert.Equal(new[] { 38671.0, 33914.0, 26762.0 }, a);
+            Assert.Equal(290, im.Width);
+            Assert.Equal(442, im.Height);
+            Assert.Equal(3, im.Bands);
         }
 
-        [SkippableFact]
-        public void TestStreamOverload()
+        if (vips810)
         {
-            Skip.IfNot(Helper.Have("jpegload_source"), "no jpeg source support, skipping test");
+            void Tiff1Valid(Image im)
+            {
+                var a = im[127, 0];
+                Assert.Equal(new[] { 0.0 }, a);
+                a = im[128, 0];
+                Assert.Equal(new[] { 255.0 }, a);
+                Assert.Equal(256, im.Width);
+                Assert.Equal(4, im.Height);
+                Assert.Equal(1, im.Bands);
+            }
 
-            // Set the beginning of the stream to an arbitrary but carefully chosen number.
-            using var stream = new MemoryStream { Position = 42 };
-            _colour.WriteToStream(stream, ".jpg");
+            FileLoader("tiffload", Helper.Tif1File, Tiff1Valid);
 
-            // Set the current position of the stream to the chosen number.
-            stream.Position = 42;
+            void Tiff2Valid(Image im)
+            {
+                var a = im[127, 0];
+                Assert.Equal(new[] { 85.0 }, a);
+                a = im[128, 0];
+                Assert.Equal(new[] { 170.0 }, a);
+                Assert.Equal(256, im.Width);
+                Assert.Equal(4, im.Height);
+                Assert.Equal(1, im.Bands);
+            }
 
-            // We should be able to read from this stream, even if it starts at any position.
-            var x = Image.NewFromStream(stream, access: Enums.Access.Sequential);
+            FileLoader("tiffload", Helper.Tif2File, Tiff2Valid);
 
-            Assert.Equal(_colour.Width, x.Width);
-            Assert.Equal(_colour.Height, x.Height);
-            Assert.Equal(_colour.Bands, x.Bands);
-            Assert.True((_colour - x).Abs().Max() <= 80);
+            void Tiff4Valid(Image im)
+            {
+                var a = im[127, 0];
+                Assert.Equal(new[] { 119.0 }, a);
+                a = im[128, 0];
+                Assert.Equal(new[] { 136.0 }, a);
+                Assert.Equal(256, im.Width);
+                Assert.Equal(4, im.Height);
+                Assert.Equal(1, im.Bands);
+            }
+
+            FileLoader("tiffload", Helper.Tif4File, Tiff4Valid);
         }
 
-        [SkippableFact]
-        public void TestTiff()
+        FileLoader("tiffload", Helper.TifFile, TiffValid);
+        BufferLoader("tiffload_buffer", Helper.TifFile, TiffValid);
+        if (NetVips.AtLeastLibvips(8, 5))
         {
-            Skip.IfNot(Helper.Have("tiffload") && File.Exists(Helper.TifFile), "no tiff support, skipping test");
+            SaveLoadBuffer("tiffsave_buffer", "tiffload_buffer", _colour);
+        }
 
-            var vips810 = NetVips.AtLeastLibvips(8, 10);
+        SaveLoad("%s.tif", _mono);
+        SaveLoad("%s.tif", _colour);
+        SaveLoad("%s.tif", _cmyk);
+
+        SaveLoad("%s.tif", _oneBit);
+        SaveLoadFile(".tif", vips810 ? "[bitdepth=1]" : "[squash]",
+            _oneBit);
+        SaveLoadFile(".tif", "[miniswhite]", _oneBit);
+        SaveLoadFile(".tif", (vips810 ? "[bitdepth=1" : "[squash") + ",miniswhite]",
+            _oneBit);
+
+        SaveLoadFile(".tif", $"[profile={Helper.SrgbFile}]", _colour);
+        SaveLoadFile(".tif", "[tile]", _colour);
+        SaveLoadFile(".tif", "[tile,pyramid]", _colour);
+        SaveLoadFile(".tif", "[tile,pyramid,compression=jpeg]", _colour, 80);
+        SaveLoadFile(".tif", "[bigtiff]", _colour);
+        SaveLoadFile(".tif", "[compression=jpeg]", _colour, 80);
+        SaveLoadFile(".tif", "[tile,tile-width=256]", _colour, 10);
+
+        if (vips810)
+        {
+            // Support for SUBIFD tags was added in libvips 8.10
+            SaveLoadFile(".tif", "[tile,pyramid,subifd]", _colour);
+            SaveLoadFile(".tif", "[tile,pyramid,subifd,compression=jpeg]", _colour, 80);
 
-            void TiffValid(Image im)
-            {
-                var a = im[10, 10];
+            // bitdepth option was added in libvips 8.10
+            var im = Image.NewFromFile(Helper.Tif2File);
+            SaveLoadFile(".tif", "[bitdepth=2]", im);
+            im = Image.NewFromFile(Helper.Tif4File);
+            SaveLoadFile(".tif", "[bitdepth=4]", im);
+        }
 
-                Assert.Equal(new[] { 38671.0, 33914.0, 26762.0 }, a);
-                Assert.Equal(290, im.Width);
-                Assert.Equal(442, im.Height);
-                Assert.Equal(3, im.Bands);
-            }
+        if (Helper.Have("tiffsave_target"))
+        {
+            // Support for tiffsave_target was added in libvips 8.13
+            SaveLoadStream(".tif", "", _colour);
 
-            if (vips810)
+            // Test Read/Seek in TargetCustom
+            using var input = File.OpenRead(Helper.GifAnimFile);
+            using var im = Image.NewFromStream(input, kwargs: new VOption
             {
-                void Tiff1Valid(Image im)
-                {
-                    var a = im[127, 0];
-                    Assert.Equal(new[] { 0.0 }, a);
-                    a = im[128, 0];
-                    Assert.Equal(new[] { 255.0 }, a);
-                    Assert.Equal(256, im.Width);
-                    Assert.Equal(4, im.Height);
-                    Assert.Equal(1, im.Bands);
-                }
+                {"n", -1}
+            });
 
-                FileLoader("tiffload", Helper.Tif1File, Tiff1Valid);
+            var tmpFile = Helper.GetTemporaryFile(_tempDir, ".tif");
+            using var output = File.Open(tmpFile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
+            im.TiffsaveStream(output);
 
-                void Tiff2Valid(Image im)
-                {
-                    var a = im[127, 0];
-                    Assert.Equal(new[] { 85.0 }, a);
-                    a = im[128, 0];
-                    Assert.Equal(new[] { 170.0 }, a);
-                    Assert.Equal(256, im.Width);
-                    Assert.Equal(4, im.Height);
-                    Assert.Equal(1, im.Bands);
-                }
-
-                FileLoader("tiffload", Helper.Tif2File, Tiff2Valid);
+            using var im2 = Image.NewFromFile(tmpFile, kwargs: new VOption
+            {
+                {"n", -1}
+            });
+            Assert.Equal(im.Width, im2.Width);
+            Assert.Equal(im.Height, im2.Height);
+            Assert.Equal(im.Bands, im2.Bands);
+            var maxDiff = (im - im2).Abs().Max();
+            Assert.Equal(0, maxDiff);
+        }
 
-                void Tiff4Valid(Image im)
-                {
-                    var a = im[127, 0];
-                    Assert.Equal(new[] { 119.0 }, a);
-                    a = im[128, 0];
-                    Assert.Equal(new[] { 136.0 }, a);
-                    Assert.Equal(256, im.Width);
-                    Assert.Equal(4, im.Height);
-                    Assert.Equal(1, im.Bands);
-                }
+        var filename = Helper.GetTemporaryFile(_tempDir, ".tif");
+        var x = Image.NewFromFile(Helper.TifFile);
+        x = x.Mutate(im => im.Set("orientation", 2));
+        x.WriteToFile(filename);
+        x = Image.NewFromFile(filename);
+        var y = x.Get("orientation");
+        Assert.Equal(2, y);
+
+        filename = Helper.GetTemporaryFile(_tempDir, ".tif");
+        x = Image.NewFromFile(Helper.TifFile);
+        x = x.Mutate(im => im.Set("orientation", 2));
+        x.WriteToFile(filename);
+        x = Image.NewFromFile(filename);
+        y = x.Get("orientation");
+        Assert.Equal(2, y);
+        x = x.Mutate(im => im.Remove("orientation"));
+
+        filename = Helper.GetTemporaryFile(_tempDir, ".tif");
+        x.WriteToFile(filename);
+        x = Image.NewFromFile(filename);
+        y = x.Get("orientation");
+        Assert.Equal(1, y);
+
+        filename = Helper.GetTemporaryFile(_tempDir, ".tif");
+        x = Image.NewFromFile(Helper.TifFile);
+        x = x.Mutate(im => im.Set("orientation", 6));
+        x.WriteToFile(filename);
+        var x1 = Image.NewFromFile(filename);
+        var x2 = Image.NewFromFile(filename, kwargs: new VOption
+        {
+            {"autorotate", true}
+        });
+        Assert.Equal(x1.Width, x2.Height);
+        Assert.Equal(x1.Height, x2.Width);
 
-                FileLoader("tiffload", Helper.Tif4File, Tiff4Valid);
-            }
+        // OME support in 8.5
+        if (NetVips.AtLeastLibvips(8, 5))
+        {
+            x = Image.NewFromFile(Helper.OmeFile);
+            Assert.Equal(439, x.Width);
+            Assert.Equal(167, x.Height);
+            var pageHeight = x.Height;
 
-            FileLoader("tiffload", Helper.TifFile, TiffValid);
-            BufferLoader("tiffload_buffer", Helper.TifFile, TiffValid);
-            if (NetVips.AtLeastLibvips(8, 5))
+            x = Image.NewFromFile(Helper.OmeFile, kwargs: new VOption
             {
-                SaveLoadBuffer("tiffsave_buffer", "tiffload_buffer", _colour);
-            }
+                {"n", -1}
+            });
+            Assert.Equal(439, x.Width);
+            Assert.Equal(pageHeight * 15, x.Height);
 
-            SaveLoad("%s.tif", _mono);
-            SaveLoad("%s.tif", _colour);
-            SaveLoad("%s.tif", _cmyk);
-
-            SaveLoad("%s.tif", _oneBit);
-            SaveLoadFile(".tif", vips810 ? "[bitdepth=1]" : "[squash]",
-                _oneBit);
-            SaveLoadFile(".tif", "[miniswhite]", _oneBit);
-            SaveLoadFile(".tif", (vips810 ? "[bitdepth=1" : "[squash") + ",miniswhite]",
-                _oneBit);
-
-            SaveLoadFile(".tif", $"[profile={Helper.SrgbFile}]", _colour);
-            SaveLoadFile(".tif", "[tile]", _colour);
-            SaveLoadFile(".tif", "[tile,pyramid]", _colour);
-            SaveLoadFile(".tif", "[tile,pyramid,compression=jpeg]", _colour, 80);
-            SaveLoadFile(".tif", "[bigtiff]", _colour);
-            SaveLoadFile(".tif", "[compression=jpeg]", _colour, 80);
-            SaveLoadFile(".tif", "[tile,tile-width=256]", _colour, 10);
-
-            if (vips810)
+            x = Image.NewFromFile(Helper.OmeFile, kwargs: new VOption
             {
-                // Support for SUBIFD tags was added in libvips 8.10
-                SaveLoadFile(".tif", "[tile,pyramid,subifd]", _colour);
-                SaveLoadFile(".tif", "[tile,pyramid,subifd,compression=jpeg]", _colour, 80);
-
-                // bitdepth option was added in libvips 8.10
-                var im = Image.NewFromFile(Helper.Tif2File);
-                SaveLoadFile(".tif", "[bitdepth=2]", im);
-                im = Image.NewFromFile(Helper.Tif4File);
-                SaveLoadFile(".tif", "[bitdepth=4]", im);
-            }
+                {"page", 1},
+                {"n", -1}
+            });
+            Assert.Equal(439, x.Width);
+            Assert.Equal(pageHeight * 14, x.Height);
 
-            if (Helper.Have("tiffsave_target"))
+            x = Image.NewFromFile(Helper.OmeFile, kwargs: new VOption
             {
-                // Support for tiffsave_target was added in libvips 8.13
-                SaveLoadStream(".tif", "", _colour);
-
-                // Test Read/Seek in TargetCustom
-                using var input = File.OpenRead(Helper.GifAnimFile);
-                using var im = Image.NewFromStream(input, kwargs: new VOption
-                {
-                    {"n", -1}
-                });
-
-                var tmpFile = Helper.GetTemporaryFile(_tempDir, ".tif");
-                using var output = File.Open(tmpFile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
-                im.TiffsaveStream(output);
-
-                using var im2 = Image.NewFromFile(tmpFile, kwargs: new VOption
-                {
-                    {"n", -1}
-                });
-                Assert.Equal(im.Width, im2.Width);
-                Assert.Equal(im.Height, im2.Height);
-                Assert.Equal(im.Bands, im2.Bands);
-                var maxDiff = (im - im2).Abs().Max();
-                Assert.Equal(0, maxDiff);
-            }
+                {"page", 1},
+                {"n", 2}
+            });
+            Assert.Equal(439, x.Width);
+            Assert.Equal(pageHeight * 2, x.Height);
 
-            var filename = Helper.GetTemporaryFile(_tempDir, ".tif");
-            var x = Image.NewFromFile(Helper.TifFile);
-            x = x.Mutate(im => im.Set("orientation", 2));
-            x.WriteToFile(filename);
-            x = Image.NewFromFile(filename);
-            var y = x.Get("orientation");
-            Assert.Equal(2, y);
+            x = Image.NewFromFile(Helper.OmeFile, kwargs: new VOption
+            {
+                {"n", -1}
+            });
+            Assert.Equal(96, x[0, 166][0]);
+            Assert.Equal(0, x[0, 167][0]);
+            Assert.Equal(1, x[0, 168][0]);
 
             filename = Helper.GetTemporaryFile(_tempDir, ".tif");
-            x = Image.NewFromFile(Helper.TifFile);
-            x = x.Mutate(im => im.Set("orientation", 2));
             x.WriteToFile(filename);
-            x = Image.NewFromFile(filename);
-            y = x.Get("orientation");
-            Assert.Equal(2, y);
-            x = x.Mutate(im => im.Remove("orientation"));
 
-            filename = Helper.GetTemporaryFile(_tempDir, ".tif");
-            x.WriteToFile(filename);
-            x = Image.NewFromFile(filename);
-            y = x.Get("orientation");
-            Assert.Equal(1, y);
+            x = Image.NewFromFile(filename, kwargs: new VOption
+            {
+                {"n", -1}
+            });
+            Assert.Equal(439, x.Width);
+            Assert.Equal(pageHeight * 15, x.Height);
+            Assert.Equal(96, x[0, 166][0]);
+            Assert.Equal(0, x[0, 167][0]);
+            Assert.Equal(1, x[0, 168][0]);
+        }
 
-            filename = Helper.GetTemporaryFile(_tempDir, ".tif");
+        // pyr save to buffer added in 8.6
+        if (NetVips.AtLeastLibvips(8, 6))
+        {
             x = Image.NewFromFile(Helper.TifFile);
-            x = x.Mutate(im => im.Set("orientation", 6));
-            x.WriteToFile(filename);
-            var x1 = Image.NewFromFile(filename);
-            var x2 = Image.NewFromFile(filename, kwargs: new VOption
+            var buf = x.TiffsaveBuffer(tile: true, pyramid: true);
+            filename = Helper.GetTemporaryFile(_tempDir, ".tif");
+            x.Tiffsave(filename, tile: true, pyramid: true);
+            var buf2 = File.ReadAllBytes(filename);
+            Assert.Equal(buf.Length, buf2.Length);
+
+            var a = Image.NewFromBuffer(buf, kwargs: new VOption
             {
-                {"autorotate", true}
+                {"page", 2}
             });
-            Assert.Equal(x1.Width, x2.Height);
-            Assert.Equal(x1.Height, x2.Width);
-
-            // OME support in 8.5
-            if (NetVips.AtLeastLibvips(8, 5))
+            var b = Image.NewFromBuffer(buf2, kwargs: new VOption
             {
-                x = Image.NewFromFile(Helper.OmeFile);
-                Assert.Equal(439, x.Width);
-                Assert.Equal(167, x.Height);
-                var pageHeight = x.Height;
+                {"page", 2}
+            });
+            Assert.Equal(a.Width, b.Width);
+            Assert.Equal(a.Height, b.Height);
+            Assert.Equal(a.Avg(), b.Avg());
+        }
 
-                x = Image.NewFromFile(Helper.OmeFile, kwargs: new VOption
-                {
-                    {"n", -1}
-                });
-                Assert.Equal(439, x.Width);
-                Assert.Equal(pageHeight * 15, x.Height);
+        // region-shrink added in 8.7
+        if (NetVips.AtLeastLibvips(8, 7))
+        {
+            x = Image.NewFromFile(Helper.TifFile);
+            _ = x.TiffsaveBuffer(tile: true, pyramid: true, regionShrink: Enums.RegionShrink.Mean);
+            _ = x.TiffsaveBuffer(tile: true, pyramid: true, regionShrink: Enums.RegionShrink.Mode);
+            _ = x.TiffsaveBuffer(tile: true, pyramid: true, regionShrink: Enums.RegionShrink.Median);
+        }
 
-                x = Image.NewFromFile(Helper.OmeFile, kwargs: new VOption
-                {
-                    {"page", 1},
-                    {"n", -1}
-                });
-                Assert.Equal(439, x.Width);
-                Assert.Equal(pageHeight * 14, x.Height);
+        // region-shrink max/min/nearest added in 8.10
+        if (vips810)
+        {
+            _ = x.TiffsaveBuffer(tile: true, pyramid: true, regionShrink: Enums.RegionShrink.Max);
+            _ = x.TiffsaveBuffer(tile: true, pyramid: true, regionShrink: Enums.RegionShrink.Min);
+            _ = x.TiffsaveBuffer(tile: true, pyramid: true, regionShrink: Enums.RegionShrink.Nearest);
+        }
+    }
 
-                x = Image.NewFromFile(Helper.OmeFile, kwargs: new VOption
-                {
-                    {"page", 1},
-                    {"n", 2}
-                });
-                Assert.Equal(439, x.Width);
-                Assert.Equal(pageHeight * 2, x.Height);
+    [SkippableFact]
+    public void TestMagickLoad()
+    {
+        Skip.IfNot(Helper.Have("magickload") &&
+                   File.Exists(Helper.BmpFile), "no magick support, skipping test");
 
-                x = Image.NewFromFile(Helper.OmeFile, kwargs: new VOption
-                {
-                    {"n", -1}
-                });
-                Assert.Equal(96, x[0, 166][0]);
-                Assert.Equal(0, x[0, 167][0]);
-                Assert.Equal(1, x[0, 168][0]);
+        void BmpValid(Image im)
+        {
+            var a = im[100, 100];
 
-                filename = Helper.GetTemporaryFile(_tempDir, ".tif");
-                x.WriteToFile(filename);
+            Helper.AssertAlmostEqualObjects(new double[] { 227, 216, 201 }, a);
+            Assert.Equal(1419, im.Width);
+            Assert.Equal(1001, im.Height);
+        }
 
-                x = Image.NewFromFile(filename, kwargs: new VOption
-                {
-                    {"n", -1}
-                });
-                Assert.Equal(439, x.Width);
-                Assert.Equal(pageHeight * 15, x.Height);
-                Assert.Equal(96, x[0, 166][0]);
-                Assert.Equal(0, x[0, 167][0]);
-                Assert.Equal(1, x[0, 168][0]);
-            }
+        FileLoader("magickload", Helper.BmpFile, BmpValid);
+        BufferLoader("magickload_buffer", Helper.BmpFile, BmpValid);
 
-            // pyr save to buffer added in 8.6
-            if (NetVips.AtLeastLibvips(8, 6))
-            {
-                x = Image.NewFromFile(Helper.TifFile);
-                var buf = x.TiffsaveBuffer(tile: true, pyramid: true);
-                filename = Helper.GetTemporaryFile(_tempDir, ".tif");
-                x.Tiffsave(filename, tile: true, pyramid: true);
-                var buf2 = File.ReadAllBytes(filename);
-                Assert.Equal(buf.Length, buf2.Length);
-
-                var a = Image.NewFromBuffer(buf, kwargs: new VOption
-                {
-                    {"page", 2}
-                });
-                var b = Image.NewFromBuffer(buf2, kwargs: new VOption
-                {
-                    {"page", 2}
-                });
-                Assert.Equal(a.Width, b.Width);
-                Assert.Equal(a.Height, b.Height);
-                Assert.Equal(a.Avg(), b.Avg());
-            }
+        // we should have rgb or rgba for svg files ... different versions of
+        // IM handle this differently. GM even gives 1 band.
+        var x = Image.Magickload(Helper.SvgFile);
+        Assert.True(x.Bands == 3 || x.Bands == 4 || x.Bands == 1);
 
-            // region-shrink added in 8.7
-            if (NetVips.AtLeastLibvips(8, 7))
-            {
-                x = Image.NewFromFile(Helper.TifFile);
-                _ = x.TiffsaveBuffer(tile: true, pyramid: true, regionShrink: Enums.RegionShrink.Mean);
-                _ = x.TiffsaveBuffer(tile: true, pyramid: true, regionShrink: Enums.RegionShrink.Mode);
-                _ = x.TiffsaveBuffer(tile: true, pyramid: true, regionShrink: Enums.RegionShrink.Median);
-            }
+        // density should change size of generated svg
+        x = Image.Magickload(Helper.SvgFile, density: "100");
+        var width = x.Width;
+        var height = x.Height;
 
-            // region-shrink max/min/nearest added in 8.10
-            if (vips810)
-            {
-                _ = x.TiffsaveBuffer(tile: true, pyramid: true, regionShrink: Enums.RegionShrink.Max);
-                _ = x.TiffsaveBuffer(tile: true, pyramid: true, regionShrink: Enums.RegionShrink.Min);
-                _ = x.TiffsaveBuffer(tile: true, pyramid: true, regionShrink: Enums.RegionShrink.Nearest);
-            }
-        }
+        // This seems to fail on travis, no idea why, some problem in their IM
+        // perhaps
+        //x = Image.Magickload(Helper.SvgFile, density: "200");
+        //Assert.Equal(width * 2, x.Width);
+        //Assert.Equal(height * 2, x.Height);
 
-        [SkippableFact]
-        public void TestMagickLoad()
+        // page/n let you pick a range of pages
+        // 'n' param added in 8.5
+        if (NetVips.AtLeastLibvips(8, 5))
         {
-            Skip.IfNot(Helper.Have("magickload") &&
-                       File.Exists(Helper.BmpFile), "no magick support, skipping test");
+            x = Image.Magickload(Helper.GifAnimFile);
+            width = x.Width;
+            height = x.Height;
+            x = Image.Magickload(Helper.GifAnimFile, page: 1, n: 2);
+            Assert.Equal(width, x.Width);
+            Assert.Equal(height * 2, x.Height);
+
+            var pageHeight = x.Get("page-height");
+            Assert.Equal(height, pageHeight);
+        }
 
-            void BmpValid(Image im)
-            {
-                var a = im[100, 100];
+        // should work for dicom
+        x = Image.Magickload(Helper.DicomFile);
+        Assert.Equal(128, x.Width);
+        Assert.Equal(128, x.Height);
 
-                Helper.AssertAlmostEqualObjects(new double[] { 227, 216, 201 }, a);
-                Assert.Equal(1419, im.Width);
-                Assert.Equal(1001, im.Height);
-            }
+        // some IMs are 3 bands, some are 1, can't really test
+        // Assert.Equal(1, x.Bands);
 
-            FileLoader("magickload", Helper.BmpFile, BmpValid);
-            BufferLoader("magickload_buffer", Helper.BmpFile, BmpValid);
+        // libvips has its own sniffer for ICO, test that
+        // added in 8.7
+        if (NetVips.AtLeastLibvips(8, 7))
+        {
+            var buf = File.ReadAllBytes(Helper.IcoFile);
+            var im = Image.NewFromBuffer(buf);
+            Assert.Equal(16, im.Width);
+            Assert.Equal(16, im.Height);
+        }
+    }
 
-            // we should have rgb or rgba for svg files ... different versions of
-            // IM handle this differently. GM even gives 1 band.
-            var x = Image.Magickload(Helper.SvgFile);
-            Assert.True(x.Bands == 3 || x.Bands == 4 || x.Bands == 1);
+    [SkippableFact]
+    public void TestMagickSave()
+    {
+        Skip.IfNot(Helper.Have("magicksave"), "no magick support, skipping test");
 
-            // density should change size of generated svg
-            x = Image.Magickload(Helper.SvgFile, density: "100");
-            var width = x.Width;
-            var height = x.Height;
+        // save to a file and load again ... we can't use SaveLoadFile since
+        // we want to make sure we use magickload/save
+        // don't use BMP - GraphicsMagick always adds an alpha
+        // don't use TIF - IM7 will save as 16-bit
+        var filename = Helper.GetTemporaryFile(_tempDir, ".jpg");
 
-            // This seems to fail on travis, no idea why, some problem in their IM
-            // perhaps
-            //x = Image.Magickload(Helper.SvgFile, density: "200");
-            //Assert.Equal(width * 2, x.Width);
-            //Assert.Equal(height * 2, x.Height);
+        _colour.Magicksave(filename);
+        var x = Image.Magickload(filename);
 
-            // page/n let you pick a range of pages
-            // 'n' param added in 8.5
-            if (NetVips.AtLeastLibvips(8, 5))
-            {
-                x = Image.Magickload(Helper.GifAnimFile);
-                width = x.Width;
-                height = x.Height;
-                x = Image.Magickload(Helper.GifAnimFile, page: 1, n: 2);
-                Assert.Equal(width, x.Width);
-                Assert.Equal(height * 2, x.Height);
-
-                var pageHeight = x.Get("page-height");
-                Assert.Equal(height, pageHeight);
-            }
+        Assert.Equal(_colour.Width, x.Width);
+        Assert.Equal(_colour.Height, x.Height);
+        Assert.Equal(_colour.Bands, x.Bands);
+        Assert.Equal(_colour.Height, x.Height);
 
-            // should work for dicom
-            x = Image.Magickload(Helper.DicomFile);
-            Assert.Equal(128, x.Width);
-            Assert.Equal(128, x.Height);
+        var maxDiff = (_colour - x).Abs().Max();
+        Assert.True(maxDiff <= 60);
 
-            // some IMs are 3 bands, some are 1, can't really test
-            // Assert.Equal(1, x.Bands);
+        SaveLoadBuffer("magicksave_buffer", "magickload_buffer", _colour, 60, new VOption
+        {
+            {"format", "JPG"}
+        });
 
-            // libvips has its own sniffer for ICO, test that
-            // added in 8.7
-            if (NetVips.AtLeastLibvips(8, 7))
+        // try an animation
+        if (Helper.Have("gifload"))
+        {
+            var x1 = Image.NewFromFile(Helper.GifAnimFile, kwargs: new VOption
             {
-                var buf = File.ReadAllBytes(Helper.IcoFile);
-                var im = Image.NewFromBuffer(buf);
-                Assert.Equal(16, im.Width);
-                Assert.Equal(16, im.Height);
-            }
-        }
+                {"n", -1}
+            });
+            var w1 = x1.MagicksaveBuffer(format: "GIF");
+            var x2 = Image.NewFromBuffer(w1, kwargs: new VOption
+            {
+                {"n", -1}
+            });
 
-        [SkippableFact]
-        public void TestMagickSave()
-        {
-            Skip.IfNot(Helper.Have("magicksave"), "no magick support, skipping test");
+            var delayName = NetVips.AtLeastLibvips(8, 9) ? "delay" : "gif-delay";
+            Assert.Equal(x2.Get(delayName), x1.Get(delayName));
+            Assert.Equal(x2.Get("page-height"), x1.Get("page-height"));
+            // magicks vary in how they handle this ... just pray we are close
+            Assert.True(Math.Abs((int)x1.Get("gif-loop") - (int)x2.Get("gif-loop")) < 5);
+        }
+    }
 
-            // save to a file and load again ... we can't use SaveLoadFile since
-            // we want to make sure we use magickload/save
-            // don't use BMP - GraphicsMagick always adds an alpha
-            // don't use TIF - IM7 will save as 16-bit
-            var filename = Helper.GetTemporaryFile(_tempDir, ".jpg");
+    [SkippableFact]
+    public void TestWebp()
+    {
+        Skip.IfNot(Helper.Have("webpload") && File.Exists(Helper.WebpFile), "no webp support, skipping test");
 
-            _colour.Magicksave(filename);
-            var x = Image.Magickload(filename);
+        void WebpValid(Image im)
+        {
+            var a = im[10, 10];
+
+            // different webp versions use different rounding systems leading
+            // to small variations
+            Helper.AssertAlmostEqualObjects(new double[] { 71, 166, 236 }, a, 2);
+            Assert.Equal(550, im.Width);
+            Assert.Equal(368, im.Height);
+            Assert.Equal(3, im.Bands);
+        }
 
-            Assert.Equal(_colour.Width, x.Width);
-            Assert.Equal(_colour.Height, x.Height);
-            Assert.Equal(_colour.Bands, x.Bands);
-            Assert.Equal(_colour.Height, x.Height);
+        FileLoader("webpload", Helper.WebpFile, WebpValid);
+        BufferLoader("webpload_buffer", Helper.WebpFile, WebpValid);
+        SaveLoadBuffer("webpsave_buffer", "webpload_buffer", _colour, 60);
+        SaveLoad("%s.webp", _colour);
+        if (Helper.Have("webpload_source"))
+        {
+            SaveLoadStream(".webp", "", _colour, 80);
+        }
 
-            var maxDiff = (_colour - x).Abs().Max();
-            Assert.True(maxDiff <= 60);
+        // test lossless mode
+        var x = Image.NewFromFile(Helper.WebpFile);
+        var buf = x.WebpsaveBuffer(lossless: true);
+        var im2 = Image.NewFromBuffer(buf);
+        Assert.Equal(0, (x - im2).Abs().Max());
+
+        // higher Q should mean a bigger buffer
+        var b1 = x.WebpsaveBuffer(q: 10);
+        var b2 = x.WebpsaveBuffer(q: 90);
+        Assert.True(b2.Length > b1.Length);
+
+        // try saving an image with an ICC profile and reading it back ... if we
+        // can do it, our webp supports metadata load/save
+        buf = _colour.WebpsaveBuffer();
+        x = Image.NewFromBuffer(buf);
+        if (x.Contains("icc-profile-data"))
+        {
+            // verify that the profile comes back unharmed
+            var p1 = _colour.Get("icc-profile-data");
+            var p2 = x.Get("icc-profile-data");
+            Assert.Equal(p1, p2);
 
-            SaveLoadBuffer("magicksave_buffer", "magickload_buffer", _colour, 60, new VOption
-            {
-                {"format", "JPG"}
-            });
+            // add tests for exif, xmp, ipct
+            // the exif test will need us to be able to walk the header,
+            // we can't just check exif-data
 
-            // try an animation
-            if (Helper.Have("gifload"))
+            // we can test that exif changes change the output of webpsave
+            // first make sure we have exif support
+            var z = Image.NewFromFile(Helper.JpegFile);
+            if (z.Contains("exif-ifd0-Orientation"))
             {
-                var x1 = Image.NewFromFile(Helper.GifAnimFile, kwargs: new VOption
-                {
-                    {"n", -1}
-                });
-                var w1 = x1.MagicksaveBuffer(format: "GIF");
-                var x2 = Image.NewFromBuffer(w1, kwargs: new VOption
-                {
-                    {"n", -1}
-                });
-
-                var delayName = NetVips.AtLeastLibvips(8, 9) ? "delay" : "gif-delay";
-                Assert.Equal(x2.Get(delayName), x1.Get(delayName));
-                Assert.Equal(x2.Get("page-height"), x1.Get("page-height"));
-                // magicks vary in how they handle this ... just pray we are close
-                Assert.True(Math.Abs((int)x1.Get("gif-loop") - (int)x2.Get("gif-loop")) < 5);
+                x = _colour.Mutate(im => im.Set("orientation", 6));
+                buf = x.WebpsaveBuffer();
+                var y = Image.NewFromBuffer(buf);
+                Assert.Equal(6, y.Get("orientation"));
             }
         }
 
-        [SkippableFact]
-        public void TestWebp()
+        // try converting an animated gif to webp ... can't do back to gif
+        // again without IM support
+        // added in 8.8, delay metadata changed in 8.9
+        if (Helper.Have("gifload") && NetVips.AtLeastLibvips(8, 9))
         {
-            Skip.IfNot(Helper.Have("webpload") && File.Exists(Helper.WebpFile), "no webp support, skipping test");
-
-            void WebpValid(Image im)
+            var x1 = Image.NewFromFile(Helper.GifAnimFile, kwargs: new VOption
             {
-                var a = im[10, 10];
-
-                // different webp versions use different rounding systems leading
-                // to small variations
-                Helper.AssertAlmostEqualObjects(new double[] { 71, 166, 236 }, a, 2);
-                Assert.Equal(550, im.Width);
-                Assert.Equal(368, im.Height);
-                Assert.Equal(3, im.Bands);
-            }
+                {"n", -1}
+            });
+            var w1 = x1.WebpsaveBuffer(q: 10);
 
-            FileLoader("webpload", Helper.WebpFile, WebpValid);
-            BufferLoader("webpload_buffer", Helper.WebpFile, WebpValid);
-            SaveLoadBuffer("webpsave_buffer", "webpload_buffer", _colour, 60);
-            SaveLoad("%s.webp", _colour);
-            if (Helper.Have("webpload_source"))
-            {
-                SaveLoadStream(".webp", "", _colour, 80);
-            }
+            var expectedDelay = (int[])x1.Get("delay");
 
-            // test lossless mode
-            var x = Image.NewFromFile(Helper.WebpFile);
-            var buf = x.WebpsaveBuffer(lossless: true);
-            var im2 = Image.NewFromBuffer(buf);
-            Assert.Equal(0, (x - im2).Abs().Max());
-
-            // higher Q should mean a bigger buffer
-            var b1 = x.WebpsaveBuffer(q: 10);
-            var b2 = x.WebpsaveBuffer(q: 90);
-            Assert.True(b2.Length > b1.Length);
-
-            // try saving an image with an ICC profile and reading it back ... if we
-            // can do it, our webp supports metadata load/save
-            buf = _colour.WebpsaveBuffer();
-            x = Image.NewFromBuffer(buf);
-            if (x.Contains("icc-profile-data"))
+            // our test gif has delay 0 for the first frame set in error,
+            // when converting to WebP this should result in a 100ms delay.
+            // see: https://github.com/libvips/libvips/pull/2145
+            if (NetVips.AtLeastLibvips(8, 10, 6))
             {
-                // verify that the profile comes back unharmed
-                var p1 = _colour.Get("icc-profile-data");
-                var p2 = x.Get("icc-profile-data");
-                Assert.Equal(p1, p2);
-
-                // add tests for exif, xmp, ipct
-                // the exif test will need us to be able to walk the header,
-                // we can't just check exif-data
-
-                // we can test that exif changes change the output of webpsave
-                // first make sure we have exif support
-                var z = Image.NewFromFile(Helper.JpegFile);
-                if (z.Contains("exif-ifd0-Orientation"))
+                for (var i = 0; i < expectedDelay.Length; i++)
                 {
-                    x = _colour.Mutate(im => im.Set("orientation", 6));
-                    buf = x.WebpsaveBuffer();
-                    var y = Image.NewFromBuffer(buf);
-                    Assert.Equal(6, y.Get("orientation"));
+                    expectedDelay[i] = expectedDelay[i] <= 10 ? 100 : expectedDelay[i];
                 }
             }
 
-            // try converting an animated gif to webp ... can't do back to gif
-            // again without IM support
-            // added in 8.8, delay metadata changed in 8.9
-            if (Helper.Have("gifload") && NetVips.AtLeastLibvips(8, 9))
+            var x2 = Image.NewFromBuffer(w1, kwargs: new VOption
             {
-                var x1 = Image.NewFromFile(Helper.GifAnimFile, kwargs: new VOption
-                {
-                    {"n", -1}
-                });
-                var w1 = x1.WebpsaveBuffer(q: 10);
-
-                var expectedDelay = (int[])x1.Get("delay");
-
-                // our test gif has delay 0 for the first frame set in error,
-                // when converting to WebP this should result in a 100ms delay.
-                // see: https://github.com/libvips/libvips/pull/2145
-                if (NetVips.AtLeastLibvips(8, 10, 6))
-                {
-                    for (var i = 0; i < expectedDelay.Length; i++)
-                    {
-                        expectedDelay[i] = expectedDelay[i] <= 10 ? 100 : expectedDelay[i];
-                    }
-                }
+                {"n", -1}
+            });
+            Assert.Equal(x1.Width, x2.Width);
+            Assert.Equal(x1.Height, x2.Height);
 
-                var x2 = Image.NewFromBuffer(w1, kwargs: new VOption
-                {
-                    {"n", -1}
-                });
-                Assert.Equal(x1.Width, x2.Width);
-                Assert.Equal(x1.Height, x2.Height);
-
-                Assert.Equal(expectedDelay, (int[])x2.Get("delay"));
-                Assert.Equal(x1.Get("page-height"), x2.Get("page-height"));
-                Assert.Equal(x1.Get("gif-loop"), x2.Get("gif-loop"));
-            }
+            Assert.Equal(expectedDelay, (int[])x2.Get("delay"));
+            Assert.Equal(x1.Get("page-height"), x2.Get("page-height"));
+            Assert.Equal(x1.Get("gif-loop"), x2.Get("gif-loop"));
         }
+    }
+
+    [SkippableFact]
+    public void TestAnalyzeLoad()
+    {
+        Skip.IfNot(Helper.Have("analyzeload") && File.Exists(Helper.AnalyzeFile),
+            "no analyze support, skipping test");
 
-        [SkippableFact]
-        public void TestAnalyzeLoad()
+        void AnalyzeValid(Image im)
         {
-            Skip.IfNot(Helper.Have("analyzeload") && File.Exists(Helper.AnalyzeFile),
-                "no analyze support, skipping test");
+            var a = im[10, 10];
 
-            void AnalyzeValid(Image im)
-            {
-                var a = im[10, 10];
+            Assert.Equal(3335, a[0]);
+            Assert.Equal(128, im.Width);
+            Assert.Equal(8064, im.Height);
+            Assert.Equal(1, im.Bands);
+        }
 
-                Assert.Equal(3335, a[0]);
-                Assert.Equal(128, im.Width);
-                Assert.Equal(8064, im.Height);
-                Assert.Equal(1, im.Bands);
-            }
+        FileLoader("analyzeload", Helper.AnalyzeFile, AnalyzeValid);
+    }
 
-            FileLoader("analyzeload", Helper.AnalyzeFile, AnalyzeValid);
-        }
+    [SkippableFact]
+    public void TestMatLoad()
+    {
+        Skip.IfNot(Helper.Have("matload") && File.Exists(Helper.MatlabFile), "no matlab support, skipping test");
 
-        [SkippableFact]
-        public void TestMatLoad()
+        void MatlabValid(Image im)
         {
-            Skip.IfNot(Helper.Have("matload") && File.Exists(Helper.MatlabFile), "no matlab support, skipping test");
+            var a = im[10, 10];
 
-            void MatlabValid(Image im)
-            {
-                var a = im[10, 10];
+            Assert.Equal(new[] { 38671.0, 33914.0, 26762.0 }, a);
+            Assert.Equal(290, im.Width);
+            Assert.Equal(442, im.Height);
+            Assert.Equal(3, im.Bands);
+        }
 
-                Assert.Equal(new[] { 38671.0, 33914.0, 26762.0 }, a);
-                Assert.Equal(290, im.Width);
-                Assert.Equal(442, im.Height);
-                Assert.Equal(3, im.Bands);
-            }
+        FileLoader("matload", Helper.MatlabFile, MatlabValid);
+    }
 
-            FileLoader("matload", Helper.MatlabFile, MatlabValid);
-        }
+    [SkippableFact]
+    public void TestOpenexrLoad()
+    {
+        Skip.IfNot(Helper.Have("openexrload") && File.Exists(Helper.ExrFile), "no openexr support, skipping test");
 
-        [SkippableFact]
-        public void TestOpenexrLoad()
+        void ExrValid(Image im)
         {
-            Skip.IfNot(Helper.Have("openexrload") && File.Exists(Helper.ExrFile), "no openexr support, skipping test");
+            var a = im[10, 10];
 
-            void ExrValid(Image im)
+            Helper.AssertAlmostEqualObjects(new[]
             {
-                var a = im[10, 10];
+                0.124512,
+                0.159668,
+                0.040375,
+                // OpenEXR alpha is scaled to 0 - 255 in libvips 8.7+
+                // but libvips 8.15+ uses alpha range of 0 - 1 for scRGB.
+                NetVips.AtLeastLibvips(8, 7) && !NetVips.AtLeastLibvips(8, 15) ? 255 : 1.0
+            }, a, 0.00001);
+            Assert.Equal(610, im.Width);
+            Assert.Equal(406, im.Height);
+            Assert.Equal(4, im.Bands);
+        }
 
-                Helper.AssertAlmostEqualObjects(new[]
-                {
-                    0.124512,
-                    0.159668,
-                    0.040375,
-                    // OpenEXR alpha is scaled to 0 - 255 in libvips 8.7+
-                    // but libvips 8.15+ uses alpha range of 0 - 1 for scRGB.
-                    NetVips.AtLeastLibvips(8, 7) && !NetVips.AtLeastLibvips(8, 15) ? 255 : 1.0
-                }, a, 0.00001);
-                Assert.Equal(610, im.Width);
-                Assert.Equal(406, im.Height);
-                Assert.Equal(4, im.Bands);
-            }
+        FileLoader("openexrload", Helper.ExrFile, ExrValid);
+    }
 
-            FileLoader("openexrload", Helper.ExrFile, ExrValid);
-        }
+    [SkippableFact]
+    public void TestFitsLoad()
+    {
+        Skip.IfNot(Helper.Have("fitsload") && File.Exists(Helper.FitsFile), "no fits support, skipping test");
 
-        [SkippableFact]
-        public void TestFitsLoad()
+        void FitsValid(Image im)
         {
-            Skip.IfNot(Helper.Have("fitsload") && File.Exists(Helper.FitsFile), "no fits support, skipping test");
+            var a = im[10, 10];
 
-            void FitsValid(Image im)
+            Helper.AssertAlmostEqualObjects(new[]
             {
-                var a = im[10, 10];
+                -0.165013,
+                -0.148553,
+                1.09122,
+                -0.942242
+            }, a, 0.00001);
+            Assert.Equal(200, im.Width);
+            Assert.Equal(200, im.Height);
+            Assert.Equal(4, im.Bands);
+        }
 
-                Helper.AssertAlmostEqualObjects(new[]
-                {
-                    -0.165013,
-                    -0.148553,
-                    1.09122,
-                    -0.942242
-                }, a, 0.00001);
-                Assert.Equal(200, im.Width);
-                Assert.Equal(200, im.Height);
-                Assert.Equal(4, im.Bands);
-            }
+        FileLoader("fitsload", Helper.FitsFile, FitsValid);
+        SaveLoad("%s.fits", _mono);
+    }
 
-            FileLoader("fitsload", Helper.FitsFile, FitsValid);
-            SaveLoad("%s.fits", _mono);
-        }
+    [SkippableFact]
+    public void TestNiftiLoad()
+    {
+        Skip.IfNot(Helper.Have("niftiload") && File.Exists(Helper.NiftiFile), "no nifti support, skipping test");
 
-        [SkippableFact]
-        public void TestNiftiLoad()
+        void NiftiValid(Image im)
         {
-            Skip.IfNot(Helper.Have("niftiload") && File.Exists(Helper.NiftiFile), "no nifti support, skipping test");
+            var a = im[30, 26];
 
-            void NiftiValid(Image im)
+            Helper.AssertAlmostEqualObjects(new[]
             {
-                var a = im[30, 26];
+                131
+            }, a);
+            Assert.Equal(91, im.Width);
+            Assert.Equal(9919, im.Height);
+            Assert.Equal(1, im.Bands);
+        }
 
-                Helper.AssertAlmostEqualObjects(new[]
-                {
-                    131
-                }, a);
-                Assert.Equal(91, im.Width);
-                Assert.Equal(9919, im.Height);
-                Assert.Equal(1, im.Bands);
-            }
+        FileLoader("niftiload", Helper.NiftiFile, NiftiValid);
+        SaveLoad("%s.nii.gz", _mono);
+    }
 
-            FileLoader("niftiload", Helper.NiftiFile, NiftiValid);
-            SaveLoad("%s.nii.gz", _mono);
-        }
+    [SkippableFact]
+    public void TestOpenslideLoad()
+    {
+        Skip.IfNot(Helper.Have("openslideload") && File.Exists(Helper.OpenslideFile),
+            "no openslide support, skipping test");
 
-        [SkippableFact]
-        public void TestOpenslideLoad()
+        void OpenslideValid(Image im)
         {
-            Skip.IfNot(Helper.Have("openslideload") && File.Exists(Helper.OpenslideFile),
-                "no openslide support, skipping test");
+            var a = im[10, 10];
 
-            void OpenslideValid(Image im)
-            {
-                var a = im[10, 10];
+            Assert.Equal(new double[] { 244, 250, 243, 255 }, a);
+            Assert.Equal(2220, im.Width);
+            Assert.Equal(2967, im.Height);
+            Assert.Equal(4, im.Bands);
+        }
 
-                Assert.Equal(new double[] { 244, 250, 243, 255 }, a);
-                Assert.Equal(2220, im.Width);
-                Assert.Equal(2967, im.Height);
-                Assert.Equal(4, im.Bands);
-            }
+        FileLoader("openslideload", Helper.OpenslideFile, OpenslideValid);
+    }
 
-            FileLoader("openslideload", Helper.OpenslideFile, OpenslideValid);
-        }
+    [SkippableFact]
+    public void TestPdfLoad()
+    {
+        Skip.IfNot(Helper.Have("pdfload") && File.Exists(Helper.PdfFile), "no pdf support, skipping test");
 
-        [SkippableFact]
-        public void TestPdfLoad()
+        void PdfValid(Image im)
         {
-            Skip.IfNot(Helper.Have("pdfload") && File.Exists(Helper.PdfFile), "no pdf support, skipping test");
+            var a = im[10, 10];
 
-            void PdfValid(Image im)
-            {
-                var a = im[10, 10];
+            Assert.Equal(new double[] { 35, 31, 32, 255 }, a);
 
-                Assert.Equal(new double[] { 35, 31, 32, 255 }, a);
+            // New sizing rules in libvips 8.8+, see:
+            // https://github.com/libvips/libvips/commit/29d29533d45848ecc12a3c50c39c26c835458a61
+            Assert.Equal(NetVips.AtLeastLibvips(8, 8) ? 1134 : 1133, im.Width);
+            Assert.Equal(680, im.Height);
+            Assert.Equal(4, im.Bands);
+        }
 
-                // New sizing rules in libvips 8.8+, see:
-                // https://github.com/libvips/libvips/commit/29d29533d45848ecc12a3c50c39c26c835458a61
-                Assert.Equal(NetVips.AtLeastLibvips(8, 8) ? 1134 : 1133, im.Width);
-                Assert.Equal(680, im.Height);
-                Assert.Equal(4, im.Bands);
-            }
+        FileLoader("pdfload", Helper.PdfFile, PdfValid);
+        BufferLoader("pdfload_buffer", Helper.PdfFile, PdfValid);
 
-            FileLoader("pdfload", Helper.PdfFile, PdfValid);
-            BufferLoader("pdfload_buffer", Helper.PdfFile, PdfValid);
+        var x = Image.NewFromFile(Helper.PdfFile);
+        var y = Image.NewFromFile(Helper.PdfFile, kwargs: new VOption
+        {
+            {"scale", 2}
+        });
+        Assert.True(Math.Abs(x.Width * 2 - y.Width) < 2);
+        Assert.True(Math.Abs(x.Height * 2 - y.Height) < 2);
 
-            var x = Image.NewFromFile(Helper.PdfFile);
-            var y = Image.NewFromFile(Helper.PdfFile, kwargs: new VOption
-            {
-                {"scale", 2}
-            });
-            Assert.True(Math.Abs(x.Width * 2 - y.Width) < 2);
-            Assert.True(Math.Abs(x.Height * 2 - y.Height) < 2);
+        x = Image.NewFromFile(Helper.PdfFile);
+        y = Image.NewFromFile(Helper.PdfFile, kwargs: new VOption
+        {
+            {"dpi", 144}
+        });
+        Assert.True(Math.Abs(x.Width * 2 - y.Width) < 2);
+        Assert.True(Math.Abs(x.Height * 2 - y.Height) < 2);
+    }
 
-            x = Image.NewFromFile(Helper.PdfFile);
-            y = Image.NewFromFile(Helper.PdfFile, kwargs: new VOption
-            {
-                {"dpi", 144}
-            });
-            Assert.True(Math.Abs(x.Width * 2 - y.Width) < 2);
-            Assert.True(Math.Abs(x.Height * 2 - y.Height) < 2);
-        }
+    [SkippableFact]
+    public void TestGifLoad()
+    {
+        Skip.IfNot(Helper.Have("gifload") && File.Exists(Helper.GifFile), "no gif support, skipping test");
 
-        [SkippableFact]
-        public void TestGifLoad()
+        void GifValid(Image im)
         {
-            Skip.IfNot(Helper.Have("gifload") && File.Exists(Helper.GifFile), "no gif support, skipping test");
-
-            void GifValid(Image im)
-            {
-                var a = im[10, 10];
+            var a = im[10, 10];
 
-                // libnsgif (vendored with libvips >= 8.11) is always RGB or RGBA
-                Assert.Equal(33, a[0]);
-                Assert.Equal(159, im.Width);
-                Assert.Equal(203, im.Height);
-                Assert.Equal(NetVips.AtLeastLibvips(8, 11) ? 3 : 1, im.Bands);
-            }
+            // libnsgif (vendored with libvips >= 8.11) is always RGB or RGBA
+            Assert.Equal(33, a[0]);
+            Assert.Equal(159, im.Width);
+            Assert.Equal(203, im.Height);
+            Assert.Equal(NetVips.AtLeastLibvips(8, 11) ? 3 : 1, im.Bands);
+        }
 
-            FileLoader("gifload", Helper.GifFile, GifValid);
-            BufferLoader("gifload_buffer", Helper.GifFile, GifValid);
+        FileLoader("gifload", Helper.GifFile, GifValid);
+        BufferLoader("gifload_buffer", Helper.GifFile, GifValid);
 
-            // test fallback stream mechanism, needs libvips >= 8.9
-            if (NetVips.AtLeastLibvips(8, 9))
+        // test fallback stream mechanism, needs libvips >= 8.9
+        if (NetVips.AtLeastLibvips(8, 9))
+        {
+            // file-based loader fallback
+            using (var input = Source.NewFromFile(Helper.GifFile))
             {
-                // file-based loader fallback
-                using (var input = Source.NewFromFile(Helper.GifFile))
-                {
-                    var img = Image.NewFromSource(input, access: Enums.Access.Sequential);
-                    GifValid(img);
-                }
-
-                // buffer-based loader fallback
-                using (var input = File.OpenRead(Helper.GifFile))
-                {
-                    var img = Image.NewFromStream(input, access: Enums.Access.Sequential);
-                    GifValid(img);
-                }
+                var img = Image.NewFromSource(input, access: Enums.Access.Sequential);
+                GifValid(img);
             }
 
-            // 'n' param added in 8.5
-            if (NetVips.AtLeastLibvips(8, 5))
+            // buffer-based loader fallback
+            using (var input = File.OpenRead(Helper.GifFile))
             {
-                var x1 = Image.NewFromFile(Helper.GifAnimFile);
-                var x2 = Image.NewFromFile(Helper.GifAnimFile, kwargs: new VOption
-                {
-                    {"n", 2}
-                });
-                Assert.Equal(2 * x1.Height, x2.Height);
-                var pageHeight = x2.Get("page-height");
-                Assert.Equal(x1.Height, pageHeight);
-
-                x2 = Image.NewFromFile(Helper.GifAnimFile, kwargs: new VOption
-                {
-                    {"n", -1}
-                });
-                Assert.Equal(5 * x1.Height, x2.Height);
-
-                // delay metadata was added in libvips 8.9
-                if (NetVips.AtLeastLibvips(8, 9))
-                {
-                    // our test gif has delay 0 for the first frame set in error
-                    Assert.Equal(new[] { 0, 50, 50, 50, 50 }, x2.Get("delay"));
-                }
-
-                x2 = Image.NewFromFile(Helper.GifAnimFile, kwargs: new VOption
-                {
-                    {"page", 1},
-                    {"n", -1}
-                });
-                Assert.Equal(4 * x1.Height, x2.Height);
+                var img = Image.NewFromStream(input, access: Enums.Access.Sequential);
+                GifValid(img);
             }
         }
 
-        [SkippableFact]
-        public void TestSvgLoad()
+        // 'n' param added in 8.5
+        if (NetVips.AtLeastLibvips(8, 5))
         {
-            Skip.IfNot(Helper.Have("svgload") && File.Exists(Helper.SvgFile), "no svg support, skipping test");
-
-            void SvgValid(Image im)
+            var x1 = Image.NewFromFile(Helper.GifAnimFile);
+            var x2 = Image.NewFromFile(Helper.GifAnimFile, kwargs: new VOption
             {
-                var a = im[10, 10];
-
-                Helper.AssertAlmostEqualObjects(new[]
-                {
-                    0, 0, 0, 0
-                }, a);
-                Assert.Equal(736, im.Width);
-                Assert.Equal(552, im.Height);
-                Assert.Equal(4, im.Bands);
-            }
-
-            FileLoader("svgload", Helper.SvgFile, SvgValid);
-            BufferLoader("svgload_buffer", Helper.SvgFile, SvgValid);
-
-            FileLoader("svgload", Helper.SvgzFile, SvgValid);
-            BufferLoader("svgload_buffer", Helper.SvgzFile, SvgValid);
-
-            FileLoader("svgload", Helper.SvgGzFile, SvgValid);
+                {"n", 2}
+            });
+            Assert.Equal(2 * x1.Height, x2.Height);
+            var pageHeight = x2.Get("page-height");
+            Assert.Equal(x1.Height, pageHeight);
 
-            var x = Image.NewFromFile(Helper.SvgFile);
-            var y = Image.NewFromFile(Helper.SvgFile, kwargs: new VOption
+            x2 = Image.NewFromFile(Helper.GifAnimFile, kwargs: new VOption
             {
-                {"scale", 2}
+                {"n", -1}
             });
+            Assert.Equal(5 * x1.Height, x2.Height);
 
-            Assert.True(Math.Abs(x.Width * 2 - y.Width) < 2);
-            Assert.True(Math.Abs(x.Height * 2 - y.Height) < 2);
+            // delay metadata was added in libvips 8.9
+            if (NetVips.AtLeastLibvips(8, 9))
+            {
+                // our test gif has delay 0 for the first frame set in error
+                Assert.Equal(new[] { 0, 50, 50, 50, 50 }, x2.Get("delay"));
+            }
 
-            x = Image.NewFromFile(Helper.SvgFile);
-            y = Image.NewFromFile(Helper.SvgFile, kwargs: new VOption
+            x2 = Image.NewFromFile(Helper.GifAnimFile, kwargs: new VOption
             {
-                {"dpi", 144}
+                {"page", 1},
+                {"n", -1}
             });
-            Assert.True(Math.Abs(x.Width * 2 - y.Width) < 2);
-            Assert.True(Math.Abs(x.Height * 2 - y.Height) < 2);
+            Assert.Equal(4 * x1.Height, x2.Height);
         }
+    }
 
-        [Fact]
-        public void TestCsv()
-        {
-            SaveLoad("%s.csv", _mono);
-        }
+    [SkippableFact]
+    public void TestSvgLoad()
+    {
+        Skip.IfNot(Helper.Have("svgload") && File.Exists(Helper.SvgFile), "no svg support, skipping test");
 
-        [SkippableFact]
-        public void TestCsvConnection()
+        void SvgValid(Image im)
         {
-            Skip.IfNot(Helper.Have("csvload_source") && Helper.Have("csvsave_target"),
-                "no CSV connection support, skipping test");
+            var a = im[10, 10];
 
-            var x = Target.NewToMemory();
-            _mono.CsvsaveTarget(x);
+            Helper.AssertAlmostEqualObjects(new[]
+            {
+                0, 0, 0, 0
+            }, a);
+            Assert.Equal(736, im.Width);
+            Assert.Equal(552, im.Height);
+            Assert.Equal(4, im.Bands);
+        }
 
-            var y = Source.NewFromMemory(x.Blob);
-            var im = Image.CsvloadSource(y);
+        FileLoader("svgload", Helper.SvgFile, SvgValid);
+        BufferLoader("svgload_buffer", Helper.SvgFile, SvgValid);
 
-            Assert.Equal(0, (im - _mono).Abs().Max());
-        }
+        FileLoader("svgload", Helper.SvgzFile, SvgValid);
+        BufferLoader("svgload_buffer", Helper.SvgzFile, SvgValid);
 
-        [Fact]
-        public void TestMatrix()
+        FileLoader("svgload", Helper.SvgGzFile, SvgValid);
+
+        var x = Image.NewFromFile(Helper.SvgFile);
+        var y = Image.NewFromFile(Helper.SvgFile, kwargs: new VOption
         {
-            SaveLoad("%s.mat", _mono);
-        }
+            {"scale", 2}
+        });
 
-        [SkippableFact]
-        public void TestMatrixConnection()
+        Assert.True(Math.Abs(x.Width * 2 - y.Width) < 2);
+        Assert.True(Math.Abs(x.Height * 2 - y.Height) < 2);
+
+        x = Image.NewFromFile(Helper.SvgFile);
+        y = Image.NewFromFile(Helper.SvgFile, kwargs: new VOption
         {
-            Skip.IfNot(Helper.Have("matrixload_source") && Helper.Have("matrixsave_target"),
-                "no matrix connection support, skipping test");
+            {"dpi", 144}
+        });
+        Assert.True(Math.Abs(x.Width * 2 - y.Width) < 2);
+        Assert.True(Math.Abs(x.Height * 2 - y.Height) < 2);
+    }
 
-            var x = Target.NewToMemory();
-            _mono.MatrixsaveTarget(x);
+    [Fact]
+    public void TestCsv()
+    {
+        SaveLoad("%s.csv", _mono);
+    }
 
-            var y = Source.NewFromMemory(x.Blob);
-            var im = Image.MatrixloadSource(y);
+    [SkippableFact]
+    public void TestCsvConnection()
+    {
+        Skip.IfNot(Helper.Have("csvload_source") && Helper.Have("csvsave_target"),
+            "no CSV connection support, skipping test");
 
-            Assert.Equal(0, (im - _mono).Abs().Max());
-        }
+        var x = Target.NewToMemory();
+        _mono.CsvsaveTarget(x);
 
-        [SkippableFact]
-        public void TestPpm()
-        {
-            Skip.IfNot(Helper.Have("ppmload"), "no PPM support, skipping test");
+        var y = Source.NewFromMemory(x.Blob);
+        var im = Image.CsvloadSource(y);
 
-            SaveLoad("%s.ppm", _mono);
-            SaveLoad("%s.ppm", _colour);
-        }
+        Assert.Equal(0, (im - _mono).Abs().Max());
+    }
 
+    [Fact]
+    public void TestMatrix()
+    {
+        SaveLoad("%s.mat", _mono);
+    }
 
-        [SkippableFact]
-        public void TestPpmConnection()
-        {
-            Skip.IfNot(Helper.Have("ppmload_source") && Helper.Have("ppmsave_target"),
-                "no PPM connection support, skipping test");
+    [SkippableFact]
+    public void TestMatrixConnection()
+    {
+        Skip.IfNot(Helper.Have("matrixload_source") && Helper.Have("matrixsave_target"),
+            "no matrix connection support, skipping test");
 
-            var x = Target.NewToMemory();
-            _mono.PpmsaveTarget(x);
+        var x = Target.NewToMemory();
+        _mono.MatrixsaveTarget(x);
 
-            var y = Source.NewFromMemory(x.Blob);
-            var im = Image.PpmloadSource(y);
+        var y = Source.NewFromMemory(x.Blob);
+        var im = Image.MatrixloadSource(y);
 
-            Assert.Equal(0, (im - _mono).Abs().Max());
-        }
+        Assert.Equal(0, (im - _mono).Abs().Max());
+    }
 
-        [SkippableFact]
-        public void TestRad()
-        {
-            Skip.IfNot(Helper.Have("radload"), "no Radiance support, skipping test");
+    [SkippableFact]
+    public void TestPpm()
+    {
+        Skip.IfNot(Helper.Have("ppmload"), "no PPM support, skipping test");
 
-            SaveLoad("%s.hdr", _colour);
-            SaveBufferTempFile("radsave_buffer", ".hdr", _rad);
+        SaveLoad("%s.ppm", _mono);
+        SaveLoad("%s.ppm", _colour);
+    }
 
-            if (Helper.Have("radload_source"))
-            {
-                SaveLoadStream(".hdr", "", _rad);
-            }
-        }
 
-        [SkippableFact]
-        public void TestDzSave()
-        {
-            Skip.IfNot(Helper.Have("dzsave"), "no dzsave support, skipping test");
-
-            // dzsave is hard to test, there are so many options
-            // test each option separately and hope they all function together
-            // correctly
-
-            // default deepzoom layout ... we must use png here, since we want to
-            // test the overlap for equality
-            var filename = Helper.GetTemporaryFile(_tempDir);
-            _colour.Dzsave(filename, suffix: ".png");
-
-            // test horizontal overlap ... expect 256 step, overlap 1
-            var x = Image.NewFromFile(filename + "_files/10/0_0.png");
-            Assert.Equal(255, x.Width);
-            var y = Image.NewFromFile(filename + "_files/10/1_0.png");
-            Assert.Equal(256, y.Width);
-
-            // the right two columns of x should equal the left two columns of y
-            var left = x.ExtractArea(x.Width - 2, 0, 2, x.Height);
-            var right = y.ExtractArea(0, 0, 2, y.Height);
-            Assert.Equal(0, (left - right).Abs().Max());
-
-            // test vertical overlap
-            Assert.Equal(255, x.Height);
-            y = Image.NewFromFile(filename + "_files/10/0_1.png");
-            Assert.Equal(256, y.Height);
-
-            // the bottom two rows of x should equal the top two rows of y
-            var top = x.ExtractArea(0, x.Height - 2, x.Width, 2);
-            var bottom = y.ExtractArea(0, 0, y.Width, 2);
-            Assert.Equal(0, (top - bottom).Abs().Max());
-
-            // there should be a bottom layer
-            x = Image.NewFromFile(filename + "_files/0/0_0.png");
-            Assert.Equal(1, x.Width);
-            Assert.Equal(1, x.Height);
-
-            // 10 should be the final layer
-            Assert.False(Directory.Exists(filename + "_files/11"));
-
-            // default google layout
-            filename = Helper.GetTemporaryFile(_tempDir);
-            _colour.Dzsave(filename, layout: Enums.ForeignDzLayout.Google);
+    [SkippableFact]
+    public void TestPpmConnection()
+    {
+        Skip.IfNot(Helper.Have("ppmload_source") && Helper.Have("ppmsave_target"),
+            "no PPM connection support, skipping test");
 
-            // test bottom-right tile ... default is 256x256 tiles, overlap 0
-            x = Image.NewFromFile(filename + "/2/2/3.jpg");
-            Assert.Equal(256, x.Width);
-            Assert.Equal(256, x.Height);
-            Assert.False(File.Exists(filename + "/2/2/4.jpg"));
-            Assert.False(Directory.Exists(filename + "/3"));
-            x = Image.NewFromFile(filename + "/blank.png");
-            Assert.Equal(256, x.Width);
-            Assert.Equal(256, x.Height);
+        var x = Target.NewToMemory();
+        _mono.PpmsaveTarget(x);
 
-            // google layout with overlap ... verify that we clip correctly
+        var y = Source.NewFromMemory(x.Blob);
+        var im = Image.PpmloadSource(y);
 
-            // overlap 1, 510x510 pixels, 256 pixel tiles, should be exactly 2x2
-            // tiles, though in fact the bottom and right edges will be white
-            filename = Helper.GetTemporaryFile(_tempDir);
+        Assert.Equal(0, (im - _mono).Abs().Max());
+    }
 
-            _colour.ExtractArea(0, 0, 510, 510).Dzsave(filename, layout: Enums.ForeignDzLayout.Google, overlap: 1,
-                depth: Enums.ForeignDzDepth.One);
+    [SkippableFact]
+    public void TestRad()
+    {
+        Skip.IfNot(Helper.Have("radload"), "no Radiance support, skipping test");
 
-            x = Image.NewFromFile(filename + "/0/1/1.jpg");
-            Assert.Equal(256, x.Width);
-            Assert.Equal(256, x.Height);
-            Assert.False(File.Exists(filename + "/0/2/2.jpg"));
+        SaveLoad("%s.hdr", _colour);
+        SaveBufferTempFile("radsave_buffer", ".hdr", _rad);
 
-            // with 511x511, it'll fit exactly into 2x2 -- we we actually generate
-            // 3x3, since we output the overlaps
-            // 8.6 revised the rules on overlaps, so don't test earlier than that
-            if (NetVips.AtLeastLibvips(8, 6))
-            {
-                filename = Helper.GetTemporaryFile(_tempDir);
-                _colour.ExtractArea(0, 0, 511, 511).Dzsave(filename, layout: Enums.ForeignDzLayout.Google, overlap: 1,
-                    depth: Enums.ForeignDzDepth.One);
-
-                x = Image.NewFromFile(filename + "/0/2/2.jpg");
-                Assert.Equal(256, x.Width);
-                Assert.Equal(256, x.Height);
-                Assert.False(File.Exists(filename + "/0/3/3.jpg"));
-            }
+        if (Helper.Have("radload_source"))
+        {
+            SaveLoadStream(".hdr", "", _rad);
+        }
+    }
 
-            // default zoomify layout
+    [SkippableFact]
+    public void TestDzSave()
+    {
+        Skip.IfNot(Helper.Have("dzsave"), "no dzsave support, skipping test");
+
+        // dzsave is hard to test, there are so many options
+        // test each option separately and hope they all function together
+        // correctly
+
+        // default deepzoom layout ... we must use png here, since we want to
+        // test the overlap for equality
+        var filename = Helper.GetTemporaryFile(_tempDir);
+        _colour.Dzsave(filename, suffix: ".png");
+
+        // test horizontal overlap ... expect 256 step, overlap 1
+        var x = Image.NewFromFile(filename + "_files/10/0_0.png");
+        Assert.Equal(255, x.Width);
+        var y = Image.NewFromFile(filename + "_files/10/1_0.png");
+        Assert.Equal(256, y.Width);
+
+        // the right two columns of x should equal the left two columns of y
+        var left = x.ExtractArea(x.Width - 2, 0, 2, x.Height);
+        var right = y.ExtractArea(0, 0, 2, y.Height);
+        Assert.Equal(0, (left - right).Abs().Max());
+
+        // test vertical overlap
+        Assert.Equal(255, x.Height);
+        y = Image.NewFromFile(filename + "_files/10/0_1.png");
+        Assert.Equal(256, y.Height);
+
+        // the bottom two rows of x should equal the top two rows of y
+        var top = x.ExtractArea(0, x.Height - 2, x.Width, 2);
+        var bottom = y.ExtractArea(0, 0, y.Width, 2);
+        Assert.Equal(0, (top - bottom).Abs().Max());
+
+        // there should be a bottom layer
+        x = Image.NewFromFile(filename + "_files/0/0_0.png");
+        Assert.Equal(1, x.Width);
+        Assert.Equal(1, x.Height);
+
+        // 10 should be the final layer
+        Assert.False(Directory.Exists(filename + "_files/11"));
+
+        // default google layout
+        filename = Helper.GetTemporaryFile(_tempDir);
+        _colour.Dzsave(filename, layout: Enums.ForeignDzLayout.Google);
+
+        // test bottom-right tile ... default is 256x256 tiles, overlap 0
+        x = Image.NewFromFile(filename + "/2/2/3.jpg");
+        Assert.Equal(256, x.Width);
+        Assert.Equal(256, x.Height);
+        Assert.False(File.Exists(filename + "/2/2/4.jpg"));
+        Assert.False(Directory.Exists(filename + "/3"));
+        x = Image.NewFromFile(filename + "/blank.png");
+        Assert.Equal(256, x.Width);
+        Assert.Equal(256, x.Height);
+
+        // google layout with overlap ... verify that we clip correctly
+
+        // overlap 1, 510x510 pixels, 256 pixel tiles, should be exactly 2x2
+        // tiles, though in fact the bottom and right edges will be white
+        filename = Helper.GetTemporaryFile(_tempDir);
+
+        _colour.ExtractArea(0, 0, 510, 510).Dzsave(filename, layout: Enums.ForeignDzLayout.Google, overlap: 1,
+            depth: Enums.ForeignDzDepth.One);
+
+        x = Image.NewFromFile(filename + "/0/1/1.jpg");
+        Assert.Equal(256, x.Width);
+        Assert.Equal(256, x.Height);
+        Assert.False(File.Exists(filename + "/0/2/2.jpg"));
+
+        // with 511x511, it'll fit exactly into 2x2 -- we we actually generate
+        // 3x3, since we output the overlaps
+        // 8.6 revised the rules on overlaps, so don't test earlier than that
+        if (NetVips.AtLeastLibvips(8, 6))
+        {
             filename = Helper.GetTemporaryFile(_tempDir);
-            _colour.Dzsave(filename, layout: Enums.ForeignDzLayout.Zoomify);
+            _colour.ExtractArea(0, 0, 511, 511).Dzsave(filename, layout: Enums.ForeignDzLayout.Google, overlap: 1,
+                depth: Enums.ForeignDzDepth.One);
 
-            // 256x256 tiles, no overlap
-            Assert.True(File.Exists(filename + "/ImageProperties.xml"));
-            x = Image.NewFromFile(filename + "/TileGroup0/2-3-2.jpg");
+            x = Image.NewFromFile(filename + "/0/2/2.jpg");
             Assert.Equal(256, x.Width);
             Assert.Equal(256, x.Height);
+            Assert.False(File.Exists(filename + "/0/3/3.jpg"));
+        }
 
-            // test zip output
-            filename = Helper.GetTemporaryFile(_tempDir, ".zip");
-            _colour.Dzsave(filename);
+        // default zoomify layout
+        filename = Helper.GetTemporaryFile(_tempDir);
+        _colour.Dzsave(filename, layout: Enums.ForeignDzLayout.Zoomify);
 
-            Assert.True(File.Exists(filename));
-            Assert.False(Directory.Exists(filename + "_files"));
-            Assert.False(File.Exists(filename + ".dzi"));
+        // 256x256 tiles, no overlap
+        Assert.True(File.Exists(filename + "/ImageProperties.xml"));
+        x = Image.NewFromFile(filename + "/TileGroup0/2-3-2.jpg");
+        Assert.Equal(256, x.Width);
+        Assert.Equal(256, x.Height);
 
-            // test compressed zip output
-            var filename2 = Helper.GetTemporaryFile(_tempDir, ".zip");
-            _colour.Dzsave(filename2, compression: -1);
+        // test zip output
+        filename = Helper.GetTemporaryFile(_tempDir, ".zip");
+        _colour.Dzsave(filename);
 
-            Assert.True(File.Exists(filename2));
+        Assert.True(File.Exists(filename));
+        Assert.False(Directory.Exists(filename + "_files"));
+        Assert.False(File.Exists(filename + ".dzi"));
 
-            var buf1 = File.ReadAllBytes(filename);
-            var buf2 = File.ReadAllBytes(filename2);
+        // test compressed zip output
+        var filename2 = Helper.GetTemporaryFile(_tempDir, ".zip");
+        _colour.Dzsave(filename2, compression: -1);
 
-            // compressed output should produce smaller file size
-            Assert.True(buf2.Length < buf1.Length);
+        Assert.True(File.Exists(filename2));
 
-            // check whether the *.dzi file is Deflate-compressed
-            Assert.Contains("http://schemas.microsoft.com/deepzoom/2008", Encoding.ASCII.GetString(buf1));
-            Assert.DoesNotContain("http://schemas.microsoft.com/deepzoom/2008", Encoding.ASCII.GetString(buf2));
+        var buf1 = File.ReadAllBytes(filename);
+        var buf2 = File.ReadAllBytes(filename2);
 
-            // test suffix
-            filename = Helper.GetTemporaryFile(_tempDir);
-            _colour.Dzsave(filename, suffix: ".png");
+        // compressed output should produce smaller file size
+        Assert.True(buf2.Length < buf1.Length);
 
-            x = Image.NewFromFile(filename + "_files/10/0_0.png");
-            Assert.Equal(255, x.Width);
+        // check whether the *.dzi file is Deflate-compressed
+        Assert.Contains("http://schemas.microsoft.com/deepzoom/2008", Encoding.ASCII.GetString(buf1));
+        Assert.DoesNotContain("http://schemas.microsoft.com/deepzoom/2008", Encoding.ASCII.GetString(buf2));
 
-            // test overlap
-            filename = Helper.GetTemporaryFile(_tempDir);
-            _colour.Dzsave(filename, overlap: 200);
+        // test suffix
+        filename = Helper.GetTemporaryFile(_tempDir);
+        _colour.Dzsave(filename, suffix: ".png");
 
-            x = Image.NewFromFile(filename + "_files/10/1_1.jpeg");
-            Assert.Equal(654, x.Width);
+        x = Image.NewFromFile(filename + "_files/10/0_0.png");
+        Assert.Equal(255, x.Width);
 
-            // test tile-size
-            filename = Helper.GetTemporaryFile(_tempDir);
-            _colour.Dzsave(filename, tileSize: 512);
+        // test overlap
+        filename = Helper.GetTemporaryFile(_tempDir);
+        _colour.Dzsave(filename, overlap: 200);
 
-            y = Image.NewFromFile(filename + "_files/10/0_0.jpeg");
-            Assert.Equal(513, y.Width);
-            Assert.Equal(513, y.Height);
+        x = Image.NewFromFile(filename + "_files/10/1_1.jpeg");
+        Assert.Equal(654, x.Width);
 
-            // test save to memory buffer
-            if (Helper.Have("dzsave_buffer"))
-            {
-                filename = Helper.GetTemporaryFile(_tempDir, ".zip");
-                var baseName = Path.GetFileNameWithoutExtension(filename);
+        // test tile-size
+        filename = Helper.GetTemporaryFile(_tempDir);
+        _colour.Dzsave(filename, tileSize: 512);
 
-                _colour.Dzsave(filename);
+        y = Image.NewFromFile(filename + "_files/10/0_0.jpeg");
+        Assert.Equal(513, y.Width);
+        Assert.Equal(513, y.Height);
 
-                buf1 = File.ReadAllBytes(filename);
-                buf2 = _colour.DzsaveBuffer(imagename: baseName);
-                Assert.Equal(buf1.Length, buf2.Length);
+        // test save to memory buffer
+        if (Helper.Have("dzsave_buffer"))
+        {
+            filename = Helper.GetTemporaryFile(_tempDir, ".zip");
+            var baseName = Path.GetFileNameWithoutExtension(filename);
 
-                // we can't test the bytes are exactly equal -- the timestamp in
-                // vips-properties.xml will be different
+            _colour.Dzsave(filename);
 
-                // added in 8.7
-                if (NetVips.AtLeastLibvips(8, 7))
-                {
-                    _ = _colour.DzsaveBuffer(regionShrink: Enums.RegionShrink.Mean);
-                    _ = _colour.DzsaveBuffer(regionShrink: Enums.RegionShrink.Mode);
-                    _ = _colour.DzsaveBuffer(regionShrink: Enums.RegionShrink.Median);
-                }
+            buf1 = File.ReadAllBytes(filename);
+            buf2 = _colour.DzsaveBuffer(imagename: baseName);
+            Assert.Equal(buf1.Length, buf2.Length);
+
+            // we can't test the bytes are exactly equal -- the timestamp in
+            // vips-properties.xml will be different
+
+            // added in 8.7
+            if (NetVips.AtLeastLibvips(8, 7))
+            {
+                _ = _colour.DzsaveBuffer(regionShrink: Enums.RegionShrink.Mean);
+                _ = _colour.DzsaveBuffer(regionShrink: Enums.RegionShrink.Mode);
+                _ = _colour.DzsaveBuffer(regionShrink: Enums.RegionShrink.Median);
             }
         }
+    }
+
+    [SkippableFact]
+    public void TestHeifload()
+    {
+        Skip.IfNot(Helper.Have("heifload"), "no HEIF support, skipping test");
 
-        [SkippableFact]
-        public void TestHeifload()
+        void HeifValid(Image im)
         {
-            Skip.IfNot(Helper.Have("heifload"), "no HEIF support, skipping test");
+            var a = im[10, 10];
 
-            void HeifValid(Image im)
+            if (NetVips.AtLeastLibvips(8, 10))
             {
-                var a = im[10, 10];
-
-                if (NetVips.AtLeastLibvips(8, 10))
+                // different versions of libheif decode have slightly different
+                // rounding
+                Helper.AssertAlmostEqualObjects(new[]
                 {
-                    // different versions of libheif decode have slightly different
-                    // rounding
-                    Helper.AssertAlmostEqualObjects(new[]
-                    {
-                        197.0, 181.0, 158.0
-                    }, a, 2);
-
-                    Assert.Equal(3024, im.Width);
-                    Assert.Equal(4032, im.Height);
-                }
-                else
+                    197.0, 181.0, 158.0
+                }, a, 2);
+
+                Assert.Equal(3024, im.Width);
+                Assert.Equal(4032, im.Height);
+            }
+            else
+            {
+                // This image has been rotated incorrectly prior to vips 8.10
+                Helper.AssertAlmostEqualObjects(new[]
                 {
-                    // This image has been rotated incorrectly prior to vips 8.10
-                    Helper.AssertAlmostEqualObjects(new[]
-                    {
-                        255, 255, 255
-                    }, a, 2);
-
-                    Assert.Equal(4032, im.Width);
-                    Assert.Equal(3024, im.Height);
-                }
+                    255, 255, 255
+                }, a, 2);
 
-                Assert.Equal(3, im.Bands);
+                Assert.Equal(4032, im.Width);
+                Assert.Equal(3024, im.Height);
             }
 
-            FileLoader("heifload", Helper.AvifFile, HeifValid);
-            BufferLoader("heifload_buffer", Helper.AvifFile, HeifValid);
+            Assert.Equal(3, im.Bands);
         }
 
-        [SkippableFact]
-        public void TestHeifsave()
-        {
-            Skip.IfNot(Helper.Have("heifsave"), "no HEIF support, skipping test");
+        FileLoader("heifload", Helper.AvifFile, HeifValid);
+        BufferLoader("heifload_buffer", Helper.AvifFile, HeifValid);
+    }
 
-            // TODO(kleisauke): Reduce the threshold once https://github.com/strukturag/libheif/issues/533 is resolved.
-            SaveLoadBuffer("heifsave_buffer", "heifload_buffer", _colour, 80, new VOption
-            {
-                {"lossless", true},
-                {"compression", Enums.ForeignHeifCompression.Av1}
-            });
+    [SkippableFact]
+    public void TestHeifsave()
+    {
+        Skip.IfNot(Helper.Have("heifsave"), "no HEIF support, skipping test");
 
-            // heifsave defaults to AV1 for .avif suffix since libvips 8.11
-            if (NetVips.AtLeastLibvips(8, 11))
-            {
-                SaveLoad("%s.avif", _colour);
-            }
-            else
-            {
-                SaveLoadFile(".avif", "[compression=av1]", _colour, 90);
-            }
+        // TODO(kleisauke): Reduce the threshold once https://github.com/strukturag/libheif/issues/533 is resolved.
+        SaveLoadBuffer("heifsave_buffer", "heifload_buffer", _colour, 80, new VOption
+        {
+            {"lossless", true},
+            {"compression", Enums.ForeignHeifCompression.Av1}
+        });
 
-            // uncomment to test lossless mode, will take a while
-            //var x = Image.NewFromFile(Helper.AvifFile);
-            //var buf = x.HeifsaveBuffer(lossless: true, compression: "av1");
-            //var im2 = Image.NewFromBuffer(buf);
+        // heifsave defaults to AV1 for .avif suffix since libvips 8.11
+        if (NetVips.AtLeastLibvips(8, 11))
+        {
+            SaveLoad("%s.avif", _colour);
+        }
+        else
+        {
+            SaveLoadFile(".avif", "[compression=av1]", _colour, 90);
+        }
 
-            // not in fact quite lossless
-            //Assert.True(Math.Abs(x.Avg() - im2.Avg()) < 3);
+        // uncomment to test lossless mode, will take a while
+        //var x = Image.NewFromFile(Helper.AvifFile);
+        //var buf = x.HeifsaveBuffer(lossless: true, compression: "av1");
+        //var im2 = Image.NewFromBuffer(buf);
 
-            // higher Q should mean a bigger buffer, needs libheif >= v1.8.0,
-            // see: https://github.com/libvips/libvips/issues/1757
-            var b1 = _mono.HeifsaveBuffer(q: 10, compression: Enums.ForeignHeifCompression.Av1);
-            var b2 = _mono.HeifsaveBuffer(q: 90, compression: Enums.ForeignHeifCompression.Av1);
-            Assert.True(b2.Length > b1.Length);
+        // not in fact quite lossless
+        //Assert.True(Math.Abs(x.Avg() - im2.Avg()) < 3);
 
-            // try saving an image with an ICC profile and reading it back
-            var buf = _colour.HeifsaveBuffer(q: 10, compression: Enums.ForeignHeifCompression.Av1);
-            var x = Image.NewFromBuffer(buf);
-            if (x.Contains("icc-profile-data"))
-            {
-                var p1 = _colour.Get("icc-profile-data");
-                var p2 = x.Get("icc-profile-data");
-                Assert.Equal(p1, p2);
-            }
+        // higher Q should mean a bigger buffer, needs libheif >= v1.8.0,
+        // see: https://github.com/libvips/libvips/issues/1757
+        var b1 = _mono.HeifsaveBuffer(q: 10, compression: Enums.ForeignHeifCompression.Av1);
+        var b2 = _mono.HeifsaveBuffer(q: 90, compression: Enums.ForeignHeifCompression.Av1);
+        Assert.True(b2.Length > b1.Length);
 
-            // add tests for exif, xmp, ipct
-            // the exif test will need us to be able to walk the header,
-            // we can't just check exif-data
+        // try saving an image with an ICC profile and reading it back
+        var buf = _colour.HeifsaveBuffer(q: 10, compression: Enums.ForeignHeifCompression.Av1);
+        var x = Image.NewFromBuffer(buf);
+        if (x.Contains("icc-profile-data"))
+        {
+            var p1 = _colour.Get("icc-profile-data");
+            var p2 = x.Get("icc-profile-data");
+            Assert.Equal(p1, p2);
+        }
 
-            // test that exif changes change the output of heifsave
-            // first make sure we have exif support
-            var z = Image.NewFromFile(Helper.AvifFile);
-            if (z.Contains("exif-ifd0-Orientation"))
-            {
-                x = z.Mutate(im => im.Set("exif-ifd0-Make", "banana"));
+        // add tests for exif, xmp, ipct
+        // the exif test will need us to be able to walk the header,
+        // we can't just check exif-data
 
-                buf = x.HeifsaveBuffer(q: 10, compression: Enums.ForeignHeifCompression.Av1);
-                var y = Image.NewFromBuffer(buf);
-                Assert.StartsWith("banana", (string)y.Get("exif-ifd0-Make"));
-            }
+        // test that exif changes change the output of heifsave
+        // first make sure we have exif support
+        var z = Image.NewFromFile(Helper.AvifFile);
+        if (z.Contains("exif-ifd0-Orientation"))
+        {
+            x = z.Mutate(im => im.Set("exif-ifd0-Make", "banana"));
+
+            buf = x.HeifsaveBuffer(q: 10, compression: Enums.ForeignHeifCompression.Av1);
+            var y = Image.NewFromBuffer(buf);
+            Assert.StartsWith("banana", (string)y.Get("exif-ifd0-Make"));
         }
     }
 }
\ No newline at end of file
diff --git a/tests/NetVips.Tests/GValueTests.cs b/tests/NetVips.Tests/GValueTests.cs
index 05bd5db4..20ddcebc 100644
--- a/tests/NetVips.Tests/GValueTests.cs
+++ b/tests/NetVips.Tests/GValueTests.cs
@@ -1,219 +1,218 @@
-namespace NetVips.Tests
+using System.IO;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace NetVips.Tests;
+
+public class GValueTests : IClassFixture
 {
-    using System.IO;
-    using Xunit;
-    using Xunit.Abstractions;
+    public GValueTests(TestsFixture testsFixture, ITestOutputHelper output)
+    {
+        testsFixture.SetUpLogging(output);
+    }
 
-    public class GValueTests : IClassFixture
+    [Fact]
+    public void TestBool()
     {
-        public GValueTests(TestsFixture testsFixture, ITestOutputHelper output)
+        bool actual;
+        using (var gv = new GValue())
         {
-            testsFixture.SetUpLogging(output);
+            gv.SetType(GValue.GBoolType);
+            gv.Set(true);
+            actual = (bool)gv.Get();
         }
 
-        [Fact]
-        public void TestBool()
-        {
-            bool actual;
-            using (var gv = new GValue())
-            {
-                gv.SetType(GValue.GBoolType);
-                gv.Set(true);
-                actual = (bool)gv.Get();
-            }
-
-            Assert.True(actual);
-        }
+        Assert.True(actual);
+    }
 
-        [Fact]
-        public void TestInt()
+    [Fact]
+    public void TestInt()
+    {
+        int actual;
+        using (var gv = new GValue())
         {
-            int actual;
-            using (var gv = new GValue())
-            {
-                gv.SetType(GValue.GIntType);
-                gv.Set(12);
-                actual = (int)gv.Get();
-            }
-
-            Assert.Equal(12, actual);
+            gv.SetType(GValue.GIntType);
+            gv.Set(12);
+            actual = (int)gv.Get();
         }
 
-        [Fact]
-        public void TestUint64()
+        Assert.Equal(12, actual);
+    }
+
+    [Fact]
+    public void TestUint64()
+    {
+        ulong actual;
+        using (var gv = new GValue())
         {
-            ulong actual;
-            using (var gv = new GValue())
-            {
-                gv.SetType(GValue.GUint64Type);
-                gv.Set(ulong.MaxValue);
-                actual = (ulong)gv.Get();
-            }
-
-            Assert.Equal(ulong.MaxValue, actual);
+            gv.SetType(GValue.GUint64Type);
+            gv.Set(ulong.MaxValue);
+            actual = (ulong)gv.Get();
         }
 
-        [Fact]
-        public void TestDouble()
+        Assert.Equal(ulong.MaxValue, actual);
+    }
+
+    [Fact]
+    public void TestDouble()
+    {
+        double actual;
+        using (var gv = new GValue())
         {
-            double actual;
-            using (var gv = new GValue())
-            {
-                gv.SetType(GValue.GDoubleType);
-                gv.Set(3.1415);
-                actual = (double)gv.Get();
-            }
-
-            Assert.Equal(3.1415, actual);
+            gv.SetType(GValue.GDoubleType);
+            gv.Set(3.1415);
+            actual = (double)gv.Get();
         }
 
-        [Fact]
-        public void TestEnum()
+        Assert.Equal(3.1415, actual);
+    }
+
+    [Fact]
+    public void TestEnum()
+    {
+        // the Interpretation enum is created when the first image is made --
+        // make it ourselves in case we are run before the first image
+        NetVips.VipsInterpretationGetType();
+        var gtype = NetVips.TypeFromName("VipsInterpretation");
+
+        Enums.Interpretation actual;
+        using (var gv = new GValue())
         {
-            // the Interpretation enum is created when the first image is made --
-            // make it ourselves in case we are run before the first image
-            NetVips.VipsInterpretationGetType();
-            var gtype = NetVips.TypeFromName("VipsInterpretation");
-
-            Enums.Interpretation actual;
-            using (var gv = new GValue())
-            {
-                gv.SetType(gtype);
-                gv.Set(Enums.Interpretation.Xyz);
-                actual = (Enums.Interpretation)gv.Get();
-            }
-
-            Assert.Equal(Enums.Interpretation.Xyz, actual);
+            gv.SetType(gtype);
+            gv.Set(Enums.Interpretation.Xyz);
+            actual = (Enums.Interpretation)gv.Get();
         }
 
-        [Fact]
-        public void TestFlags()
+        Assert.Equal(Enums.Interpretation.Xyz, actual);
+    }
+
+    [Fact]
+    public void TestFlags()
+    {
+        // the OperationFlags enum is created when the first op is made --
+        // make it ourselves in case we are run before that
+        NetVips.VipsOperationFlagsGetType();
+        var gtype = NetVips.TypeFromName("VipsOperationFlags");
+
+        Enums.OperationFlags actual;
+        using (var gv = new GValue())
         {
-            // the OperationFlags enum is created when the first op is made --
-            // make it ourselves in case we are run before that
-            NetVips.VipsOperationFlagsGetType();
-            var gtype = NetVips.TypeFromName("VipsOperationFlags");
-
-            Enums.OperationFlags actual;
-            using (var gv = new GValue())
-            {
-                gv.SetType(gtype);
-                gv.Set(Enums.OperationFlags.DEPRECATED);
-                actual = (Enums.OperationFlags)gv.Get();
-            }
-
-            Assert.Equal(Enums.OperationFlags.DEPRECATED, actual);
+            gv.SetType(gtype);
+            gv.Set(Enums.OperationFlags.DEPRECATED);
+            actual = (Enums.OperationFlags)gv.Get();
         }
 
-        [Fact]
-        public void TestString()
+        Assert.Equal(Enums.OperationFlags.DEPRECATED, actual);
+    }
+
+    [Fact]
+    public void TestString()
+    {
+        string actual;
+        using (var gv = new GValue())
         {
-            string actual;
-            using (var gv = new GValue())
-            {
-                gv.SetType(GValue.GStrType);
-                gv.Set("banana");
-                actual = (string)gv.Get();
-            }
-
-            Assert.Equal("banana", actual);
+            gv.SetType(GValue.GStrType);
+            gv.Set("banana");
+            actual = (string)gv.Get();
         }
 
-        [Fact]
-        public void TestRefString()
+        Assert.Equal("banana", actual);
+    }
+
+    [Fact]
+    public void TestRefString()
+    {
+        string actual;
+        using (var gv = new GValue())
         {
-            string actual;
-            using (var gv = new GValue())
-            {
-                gv.SetType(GValue.RefStrType);
-                gv.Set("banana");
-                actual = (string)gv.Get();
-            }
-
-            Assert.Equal("banana", actual);
+            gv.SetType(GValue.RefStrType);
+            gv.Set("banana");
+            actual = (string)gv.Get();
         }
 
-        [Fact]
-        public void TestArrayInt()
+        Assert.Equal("banana", actual);
+    }
+
+    [Fact]
+    public void TestArrayInt()
+    {
+        int[] actual;
+        using (var gv = new GValue())
         {
-            int[] actual;
-            using (var gv = new GValue())
-            {
-                gv.SetType(GValue.ArrayIntType);
-                gv.Set(new[] { 1, 2, 3 });
-                actual = (int[])gv.Get();
-            }
-
-            Assert.Equal(new[] { 1, 2, 3 }, actual);
+            gv.SetType(GValue.ArrayIntType);
+            gv.Set(new[] { 1, 2, 3 });
+            actual = (int[])gv.Get();
         }
 
-        [Fact]
-        public void TestArrayDouble()
+        Assert.Equal(new[] { 1, 2, 3 }, actual);
+    }
+
+    [Fact]
+    public void TestArrayDouble()
+    {
+        double[] actual;
+        using (var gv = new GValue())
         {
-            double[] actual;
-            using (var gv = new GValue())
-            {
-                gv.SetType(GValue.ArrayDoubleType);
-                gv.Set(new[] { 1.1, 2.1, 3.1 });
-                actual = (double[])gv.Get();
-            }
-
-            Assert.Equal(new[] { 1.1, 2.1, 3.1 }, actual);
+            gv.SetType(GValue.ArrayDoubleType);
+            gv.Set(new[] { 1.1, 2.1, 3.1 });
+            actual = (double[])gv.Get();
         }
 
-        [Fact]
-        public void TestImage()
+        Assert.Equal(new[] { 1.1, 2.1, 3.1 }, actual);
+    }
+
+    [Fact]
+    public void TestImage()
+    {
+        var image = Image.NewFromFile(Helper.JpegFile);
+
+        Image actual;
+        using (var gv = new GValue())
         {
-            var image = Image.NewFromFile(Helper.JpegFile);
+            gv.SetType(GValue.ImageType);
+            gv.Set(image);
+            actual = (Image)gv.Get();
+        }
 
-            Image actual;
-            using (var gv = new GValue())
-            {
-                gv.SetType(GValue.ImageType);
-                gv.Set(image);
-                actual = (Image)gv.Get();
-            }
 
+        Assert.Equal(image, actual);
+    }
 
-            Assert.Equal(image, actual);
-        }
+    [Fact]
+    public void TestArrayImage()
+    {
+        var images = Image.NewFromFile(Helper.JpegFile).Bandsplit();
 
-        [Fact]
-        public void TestArrayImage()
+        Image[] actualImages;
+        using (var gv = new GValue())
         {
-            var images = Image.NewFromFile(Helper.JpegFile).Bandsplit();
-
-            Image[] actualImages;
-            using (var gv = new GValue())
-            {
-                gv.SetType(GValue.ArrayImageType);
-                gv.Set(images);
-                actualImages = (Image[])gv.Get();
-            }
-
-            for (var i = 0; i < actualImages.Length; i++)
-            {
-                var actual = actualImages[i];
-                var expected = images[i];
-
-                Assert.Equal(expected, actual);
-            }
+            gv.SetType(GValue.ArrayImageType);
+            gv.Set(images);
+            actualImages = (Image[])gv.Get();
         }
 
-        [Fact]
-        public void TestBlob()
+        for (var i = 0; i < actualImages.Length; i++)
         {
-            var blob = File.ReadAllBytes(Helper.JpegFile);
-            byte[] actual;
+            var actual = actualImages[i];
+            var expected = images[i];
 
-            using (var gv = new GValue())
-            {
-                gv.SetType(GValue.BlobType);
-                gv.Set(blob);
-                actual = (byte[])gv.Get();
-            }
+            Assert.Equal(expected, actual);
+        }
+    }
+
+    [Fact]
+    public void TestBlob()
+    {
+        var blob = File.ReadAllBytes(Helper.JpegFile);
+        byte[] actual;
 
-            Assert.Equal(blob, actual);
+        using (var gv = new GValue())
+        {
+            gv.SetType(GValue.BlobType);
+            gv.Set(blob);
+            actual = (byte[])gv.Get();
         }
+
+        Assert.Equal(blob, actual);
     }
 }
\ No newline at end of file
diff --git a/tests/NetVips.Tests/Helper.cs b/tests/NetVips.Tests/Helper.cs
index 6c253fdf..e231b3cb 100644
--- a/tests/NetVips.Tests/Helper.cs
+++ b/tests/NetVips.Tests/Helper.cs
@@ -1,470 +1,469 @@
-namespace NetVips.Tests
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Xunit;
+
+namespace NetVips.Tests;
+
+public static class Helper
 {
-    using System;
-    using System.Collections;
-    using System.Collections.Generic;
-    using System.IO;
-    using System.Linq;
-    using Xunit;
-
-    public static class Helper
+    public static readonly string Images = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "images");
+
+    public static readonly string JpegFile = Path.Combine(Images, "йцук.jpg");
+    public static readonly string TruncatedFile = Path.Combine(Images, "truncated.jpg");
+    public static readonly string SrgbFile = Path.Combine(Images, "sRGB.icm");
+    public static readonly string MatlabFile = Path.Combine(Images, "sample.mat");
+    public static readonly string PngFile = Path.Combine(Images, "sample.png");
+    public static readonly string TifFile = Path.Combine(Images, "sample.tif");
+    public static readonly string Tif1File = Path.Combine(Images, "1bit.tif");
+    public static readonly string Tif2File = Path.Combine(Images, "2bit.tif");
+    public static readonly string Tif4File = Path.Combine(Images, "4bit.tif");
+    public static readonly string OmeFile = Path.Combine(Images, "multi-channel-z-series.ome.tif");
+    public static readonly string AnalyzeFile = Path.Combine(Images, "t00740_tr1_segm.hdr");
+    public static readonly string GifFile = Path.Combine(Images, "cramps.gif");
+    public static readonly string WebpFile = Path.Combine(Images, "1.webp");
+    public static readonly string ExrFile = Path.Combine(Images, "sample.exr");
+    public static readonly string FitsFile = Path.Combine(Images, "WFPC2u5780205r_c0fx.fits");
+    public static readonly string OpenslideFile = Path.Combine(Images, "CMU-1-Small-Region.svs");
+    public static readonly string PdfFile = Path.Combine(Images, "ISO_12233-reschart.pdf");
+    public static readonly string SvgFile = Path.Combine(Images, "logo.svg");
+    public static readonly string SvgzFile = Path.Combine(Images, "logo.svgz");
+    public static readonly string SvgGzFile = Path.Combine(Images, "logo.svg.gz");
+    public static readonly string GifAnimFile = Path.Combine(Images, "cogs.gif");
+    public static readonly string DicomFile = Path.Combine(Images, "dicom_test_image.dcm");
+    public static readonly string BmpFile = Path.Combine(Images, "MARBLES.BMP");
+    public static readonly string NiftiFile = Path.Combine(Images, "avg152T1_LR_nifti.nii.gz");
+    public static readonly string IcoFile = Path.Combine(Images, "favicon.ico");
+    public static readonly string AvifFile = Path.Combine(Images, "avif-orientation-6.avif");
+
+    public static readonly Enums.BandFormat[] UnsignedFormats =
+    {
+        Enums.BandFormat.Uchar,
+        Enums.BandFormat.Ushort,
+        Enums.BandFormat.Uint
+    };
+
+    public static readonly Enums.BandFormat[] SignedFormats =
+    {
+        Enums.BandFormat.Char,
+        Enums.BandFormat.Short,
+        Enums.BandFormat.Int
+    };
+
+    public static readonly Enums.BandFormat[] FloatFormats =
+    {
+        Enums.BandFormat.Float,
+        Enums.BandFormat.Double
+    };
+
+    public static readonly Enums.BandFormat[] ComplexFormats =
+    {
+        Enums.BandFormat.Complex,
+        Enums.BandFormat.Dpcomplex
+    };
+
+    public static readonly Enums.BandFormat[] IntFormats = UnsignedFormats.Concat(SignedFormats).ToArray();
+
+    public static readonly Enums.BandFormat[] NonComplexFormats = IntFormats.Concat(FloatFormats).ToArray();
+
+    public static readonly Enums.BandFormat[] AllFormats = IntFormats.Concat(FloatFormats).Concat(ComplexFormats).ToArray();
+
+    public static readonly Enums.Interpretation[] ColourColourspaces =
+    {
+        Enums.Interpretation.Xyz,
+        Enums.Interpretation.Lab,
+        Enums.Interpretation.Lch,
+        Enums.Interpretation.Cmc,
+        Enums.Interpretation.Labs,
+        Enums.Interpretation.Scrgb,
+        Enums.Interpretation.Hsv,
+        Enums.Interpretation.Srgb,
+        Enums.Interpretation.Yxy
+    };
+
+    public static readonly Enums.Interpretation[] CodedColourspaces =
+    {
+        Enums.Interpretation.Labq
+    };
+
+    public static readonly Enums.Interpretation[] MonoColourspaces =
+    {
+        Enums.Interpretation.Bw
+    };
+
+    public static readonly Enums.Interpretation[] SixteenbitColourspaces =
+    {
+        Enums.Interpretation.Grey16,
+        Enums.Interpretation.Rgb16
+    };
+
+    public static readonly Enums.Interpretation[] CmykColourspaces =
+    {
+        Enums.Interpretation.Cmyk
+    };
+
+    public static Enums.Interpretation[] AllColourspaces = ColourColourspaces.Concat(MonoColourspaces)
+        .Concat(CodedColourspaces)
+        .Concat(SixteenbitColourspaces)
+        .Concat(CmykColourspaces)
+        .ToArray();
+
+    public static readonly Dictionary MaxValue = new()
     {
-        public static readonly string Images = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "images");
-
-        public static readonly string JpegFile = Path.Combine(Images, "йцук.jpg");
-        public static readonly string TruncatedFile = Path.Combine(Images, "truncated.jpg");
-        public static readonly string SrgbFile = Path.Combine(Images, "sRGB.icm");
-        public static readonly string MatlabFile = Path.Combine(Images, "sample.mat");
-        public static readonly string PngFile = Path.Combine(Images, "sample.png");
-        public static readonly string TifFile = Path.Combine(Images, "sample.tif");
-        public static readonly string Tif1File = Path.Combine(Images, "1bit.tif");
-        public static readonly string Tif2File = Path.Combine(Images, "2bit.tif");
-        public static readonly string Tif4File = Path.Combine(Images, "4bit.tif");
-        public static readonly string OmeFile = Path.Combine(Images, "multi-channel-z-series.ome.tif");
-        public static readonly string AnalyzeFile = Path.Combine(Images, "t00740_tr1_segm.hdr");
-        public static readonly string GifFile = Path.Combine(Images, "cramps.gif");
-        public static readonly string WebpFile = Path.Combine(Images, "1.webp");
-        public static readonly string ExrFile = Path.Combine(Images, "sample.exr");
-        public static readonly string FitsFile = Path.Combine(Images, "WFPC2u5780205r_c0fx.fits");
-        public static readonly string OpenslideFile = Path.Combine(Images, "CMU-1-Small-Region.svs");
-        public static readonly string PdfFile = Path.Combine(Images, "ISO_12233-reschart.pdf");
-        public static readonly string SvgFile = Path.Combine(Images, "logo.svg");
-        public static readonly string SvgzFile = Path.Combine(Images, "logo.svgz");
-        public static readonly string SvgGzFile = Path.Combine(Images, "logo.svg.gz");
-        public static readonly string GifAnimFile = Path.Combine(Images, "cogs.gif");
-        public static readonly string DicomFile = Path.Combine(Images, "dicom_test_image.dcm");
-        public static readonly string BmpFile = Path.Combine(Images, "MARBLES.BMP");
-        public static readonly string NiftiFile = Path.Combine(Images, "avg152T1_LR_nifti.nii.gz");
-        public static readonly string IcoFile = Path.Combine(Images, "favicon.ico");
-        public static readonly string AvifFile = Path.Combine(Images, "avif-orientation-6.avif");
-
-        public static readonly Enums.BandFormat[] UnsignedFormats =
         {
             Enums.BandFormat.Uchar,
+            0xff
+        },
+        {
             Enums.BandFormat.Ushort,
-            Enums.BandFormat.Uint
-        };
-
-        public static readonly Enums.BandFormat[] SignedFormats =
+            0xffff
+        },
+        {
+            Enums.BandFormat.Uint,
+            0xffffffff
+        },
         {
             Enums.BandFormat.Char,
+            0x7f
+        },
+        {
             Enums.BandFormat.Short,
-            Enums.BandFormat.Int
-        };
-
-        public static readonly Enums.BandFormat[] FloatFormats =
+            0x7fff
+        },
+        {
+            Enums.BandFormat.Int,
+            0x7fffffff
+        },
         {
             Enums.BandFormat.Float,
-            Enums.BandFormat.Double
-        };
-
-        public static readonly Enums.BandFormat[] ComplexFormats =
+            1.0
+        },
         {
-            Enums.BandFormat.Complex,
-            Enums.BandFormat.Dpcomplex
-        };
-
-        public static readonly Enums.BandFormat[] IntFormats = UnsignedFormats.Concat(SignedFormats).ToArray();
-
-        public static readonly Enums.BandFormat[] NonComplexFormats = IntFormats.Concat(FloatFormats).ToArray();
-
-        public static readonly Enums.BandFormat[] AllFormats = IntFormats.Concat(FloatFormats).Concat(ComplexFormats).ToArray();
-
-        public static readonly Enums.Interpretation[] ColourColourspaces =
+            Enums.BandFormat.Double,
+            1.0
+        },
         {
-            Enums.Interpretation.Xyz,
-            Enums.Interpretation.Lab,
-            Enums.Interpretation.Lch,
-            Enums.Interpretation.Cmc,
-            Enums.Interpretation.Labs,
-            Enums.Interpretation.Scrgb,
-            Enums.Interpretation.Hsv,
-            Enums.Interpretation.Srgb,
-            Enums.Interpretation.Yxy
-        };
-
-        public static readonly Enums.Interpretation[] CodedColourspaces =
+            Enums.BandFormat.Complex,
+            1.0
+        },
         {
-            Enums.Interpretation.Labq
-        };
+            Enums.BandFormat.Dpcomplex,
+            1.0
+        }
+    };
 
-        public static readonly Enums.Interpretation[] MonoColourspaces =
+    public static readonly Dictionary SizeOfFormat = new()
+    {
         {
-            Enums.Interpretation.Bw
-        };
-
-        public static readonly Enums.Interpretation[] SixteenbitColourspaces =
+            Enums.BandFormat.Uchar,
+            1
+        },
         {
-            Enums.Interpretation.Grey16,
-            Enums.Interpretation.Rgb16
-        };
-
-        public static readonly Enums.Interpretation[] CmykColourspaces =
+            Enums.BandFormat.Ushort,
+            2
+        },
         {
-            Enums.Interpretation.Cmyk
-        };
-
-        public static Enums.Interpretation[] AllColourspaces = ColourColourspaces.Concat(MonoColourspaces)
-            .Concat(CodedColourspaces)
-            .Concat(SixteenbitColourspaces)
-            .Concat(CmykColourspaces)
-            .ToArray();
-
-        public static readonly Dictionary MaxValue = new()
+            Enums.BandFormat.Uint,
+            4
+        },
         {
-            {
-                Enums.BandFormat.Uchar,
-                0xff
-            },
-            {
-                Enums.BandFormat.Ushort,
-                0xffff
-            },
-            {
-                Enums.BandFormat.Uint,
-                0xffffffff
-            },
-            {
-                Enums.BandFormat.Char,
-                0x7f
-            },
-            {
-                Enums.BandFormat.Short,
-                0x7fff
-            },
-            {
-                Enums.BandFormat.Int,
-                0x7fffffff
-            },
-            {
-                Enums.BandFormat.Float,
-                1.0
-            },
-            {
-                Enums.BandFormat.Double,
-                1.0
-            },
-            {
-                Enums.BandFormat.Complex,
-                1.0
-            },
-            {
-                Enums.BandFormat.Dpcomplex,
-                1.0
-            }
-        };
-
-        public static readonly Dictionary SizeOfFormat = new()
+            Enums.BandFormat.Char,
+            1
+        },
         {
-            {
-                Enums.BandFormat.Uchar,
-                1
-            },
-            {
-                Enums.BandFormat.Ushort,
-                2
-            },
-            {
-                Enums.BandFormat.Uint,
-                4
-            },
-            {
-                Enums.BandFormat.Char,
-                1
-            },
-            {
-                Enums.BandFormat.Short,
-                2
-            },
-            {
-                Enums.BandFormat.Int,
-                4
-            },
-            {
-                Enums.BandFormat.Float,
-                4
-            },
-            {
-                Enums.BandFormat.Double,
-                8
-            },
-            {
-                Enums.BandFormat.Complex,
-                8
-            },
-            {
-                Enums.BandFormat.Dpcomplex,
-                16
-            }
-        };
-
-        public static readonly Enums.Angle45[] Rot45Angles =
+            Enums.BandFormat.Short,
+            2
+        },
         {
-            Enums.Angle45.D0,
-            Enums.Angle45.D45,
-            Enums.Angle45.D90,
-            Enums.Angle45.D135,
-            Enums.Angle45.D180,
-            Enums.Angle45.D225,
-            Enums.Angle45.D270,
-            Enums.Angle45.D315
-        };
-
-        public static readonly Enums.Angle45[] Rot45AngleBonds =
+            Enums.BandFormat.Int,
+            4
+        },
         {
-            Enums.Angle45.D0,
-            Enums.Angle45.D315,
-            Enums.Angle45.D270,
-            Enums.Angle45.D225,
-            Enums.Angle45.D180,
-            Enums.Angle45.D135,
-            Enums.Angle45.D90,
-            Enums.Angle45.D45
-        };
-
-        public static readonly Enums.Angle[] RotAngles =
+            Enums.BandFormat.Float,
+            4
+        },
         {
-            Enums.Angle.D0,
-            Enums.Angle.D90,
-            Enums.Angle.D180,
-            Enums.Angle.D270
-        };
-
-        public static readonly Enums.Angle[] RotAngleBonds =
+            Enums.BandFormat.Double,
+            8
+        },
         {
-            Enums.Angle.D0,
-            Enums.Angle.D270,
-            Enums.Angle.D180,
-            Enums.Angle.D90
-        };
-
-        /// 
-        /// an expanding zip ... if either of the args is a scalar or a one-element list,
-        /// duplicate it down the other side
-        /// 
-        /// 
-        /// 
-        /// 
-        public static IEnumerable ZipExpand(object x, object y)
+            Enums.BandFormat.Complex,
+            8
+        },
         {
-            // handle singleton list case
-            if (x is Array xArray && xArray.Length == 1)
-            {
-                x = xArray.GetValue(0);
-            }
-
-            if (y is Array yArray && yArray.Length == 1)
-            {
-                y = yArray.GetValue(0);
-            }
-
-            if (x is IEnumerable enumerable1 && y is IEnumerable enumerable2)
-            {
-                return enumerable1.Cast()
-                    .Zip(enumerable2.Cast(), (xObj, yObj) => new[] { xObj, yObj })
-                    .ToArray();
-            }
-
-            if (x is IEnumerable enumerableX)
-            {
-                return enumerableX.Cast().Select(i => new[]
-                {
-                    i,
-                    y
-                }).ToArray();
-            }
-
-            if (y is IEnumerable enumerableY)
-            {
-                return enumerableY.Cast().Select(j => new[]
-                {
-                    x,
-                    j
-                }).ToArray();
-            }
-
-            return new[]
-            {
-                new[] {x, y}
-            };
+            Enums.BandFormat.Dpcomplex,
+            16
         }
+    };
 
-        /// 
-        /// run a 1-ary function on a thing -- loop over elements if the
-        /// thing is a list
-        /// 
-        /// 
-        /// 
-        public static object RunFn(Func func, object x)
-        {
-            if (x is IEnumerable enumerable)
-            {
-                return enumerable.Cast().Select(func).ToArray();
-            }
-
-            return func(x);
-        }
+    public static readonly Enums.Angle45[] Rot45Angles =
+    {
+        Enums.Angle45.D0,
+        Enums.Angle45.D45,
+        Enums.Angle45.D90,
+        Enums.Angle45.D135,
+        Enums.Angle45.D180,
+        Enums.Angle45.D225,
+        Enums.Angle45.D270,
+        Enums.Angle45.D315
+    };
+
+    public static readonly Enums.Angle45[] Rot45AngleBonds =
+    {
+        Enums.Angle45.D0,
+        Enums.Angle45.D315,
+        Enums.Angle45.D270,
+        Enums.Angle45.D225,
+        Enums.Angle45.D180,
+        Enums.Angle45.D135,
+        Enums.Angle45.D90,
+        Enums.Angle45.D45
+    };
+
+    public static readonly Enums.Angle[] RotAngles =
+    {
+        Enums.Angle.D0,
+        Enums.Angle.D90,
+        Enums.Angle.D180,
+        Enums.Angle.D270
+    };
 
-        /// 
-        /// run a 2-ary function on two things -- loop over elements pairwise if the
-        /// things are lists
-        /// 
-        /// 
-        /// 
-        /// 
-        /// 
-        public static object RunFn2(Func func, object x, object y)
+    public static readonly Enums.Angle[] RotAngleBonds =
+    {
+        Enums.Angle.D0,
+        Enums.Angle.D270,
+        Enums.Angle.D180,
+        Enums.Angle.D90
+    };
+
+    /// 
+    /// an expanding zip ... if either of the args is a scalar or a one-element list,
+    /// duplicate it down the other side
+    /// 
+    /// 
+    /// 
+    /// 
+    public static IEnumerable ZipExpand(object x, object y)
+    {
+        // handle singleton list case
+        if (x is Array xArray && xArray.Length == 1)
         {
-            if (x is Image || y is Image)
-            {
-                return func(x, y);
-            }
-
-            if (x is Array || y is Array)
-            {
-                return ZipExpand(x, y).Select(o => func(o[0], o[1])).ToArray();
-            }
-
-            return func(x, y);
+            x = xArray.GetValue(0);
         }
 
-        /// 
-        /// run a function on an image and on a single pixel, the results
-        /// should match
-        /// 
-        /// 
-        /// 
-        /// 
-        /// 
-        public static void RunCmp(Image im, int x, int y, Func func)
+        if (y is Array yArray && yArray.Length == 1)
         {
-            var a = im[x, y];
-            var v1 = func(a);
-            var im2 = (Image)func(im);
-            var v2 = im2[x, y];
-
-            AssertAlmostEqualObjects(v1 is IEnumerable enumerable ? enumerable : new[] { v1 }, v2);
+            y = yArray.GetValue(0);
         }
 
-        /// 
-        /// run a function on a pair of images and on a pair of pixels, the results
-        /// should match
-        /// 
-        /// 
-        /// 
-        /// 
-        /// 
-        /// 
-        /// 
-        public static void RunCmp2(Image left, Image right, int x, int y, Func func)
+        if (x is IEnumerable enumerable1 && y is IEnumerable enumerable2)
         {
-            var a = left[x, y];
-            var b = right[x, y];
-            var v1 = func(a, b);
-            var after = (Image)func(left, right);
-            var v2 = after[x, y];
-
-            AssertAlmostEqualObjects(v1 is IEnumerable enumerable ? enumerable : new[] { v1 }, v2);
+            return enumerable1.Cast()
+                .Zip(enumerable2.Cast(), (xObj, yObj) => new[] { xObj, yObj })
+                .ToArray();
         }
 
-        /// 
-        /// run a function on a pair of images
-        /// 50,50 and 10,10 should have different values on the test image
-        /// 
-        /// 
-        /// 
-        /// 
-        /// 
-        public static void RunImage2(Image left, Image right, Func func)
+        if (x is IEnumerable enumerableX)
         {
-            RunCmp2(left, right, 50, 50, (x, y) => RunFn2(func, x, y));
-            RunCmp2(left, right, 10, 10, (x, y) => RunFn2(func, x, y));
+            return enumerableX.Cast().Select(i => new[]
+            {
+                i,
+                y
+            }).ToArray();
         }
 
-        /// 
-        /// run a function on (image, constant), and on (constant, image).
-        /// 50,50 and 10,10 should have different values on the test image
-        /// 
-        public static void RunConst(Func func, Image im, object c)
+        if (y is IEnumerable enumerableY)
         {
-            RunCmp(im, 50, 50, x => RunFn2(func, x, c));
-            RunCmp(im, 50, 50, x => RunFn2(func, c, x));
-            RunCmp(im, 10, 10, x => RunFn2(func, x, c));
-            RunCmp(im, 10, 10, x => RunFn2(func, c, x));
+            return enumerableY.Cast().Select(j => new[]
+            {
+                x,
+                j
+            }).ToArray();
         }
 
-        /// 
-        /// test a pair of things which can be lists for approx. equality
-        /// 
-        /// 
-        /// 
-        /// 
-        public static void AssertAlmostEqualObjects(IEnumerable expected, IEnumerable actual, double delta = 0.0001)
+        return new[]
         {
-            Assert.True(expected.Cast().SequenceEqual(actual.Cast(), new ObjectComparerDelta(delta)));
-        }
+            new[] {x, y}
+        };
+    }
 
-        /// 
-        /// test a pair of things which can be lists for difference less than a
-        /// thresholdy
-        /// 
-        /// 
-        /// 
-        /// 
-        public static void AssertLessThreshold(object a, object b, double diff)
+    /// 
+    /// run a 1-ary function on a thing -- loop over elements if the
+    /// thing is a list
+    /// 
+    /// 
+    /// 
+    public static object RunFn(Func func, object x)
+    {
+        if (x is IEnumerable enumerable)
         {
-            foreach (var expand in ZipExpand(a, b))
-            {
-                var x = Convert.ToDouble(expand[0]);
-                var y = Convert.ToDouble(expand[1]);
-                Assert.True(Math.Abs(x - y) < diff);
-            }
+            return enumerable.Cast().Select(func).ToArray();
         }
 
-        public static string GetTemporaryDirectory()
-        {
-            var tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
-            Directory.CreateDirectory(tempDirectory);
-            return tempDirectory;
-        }
+        return func(x);
+    }
 
-        public static string GetTemporaryFile(string path, string extension = "")
+    /// 
+    /// run a 2-ary function on two things -- loop over elements pairwise if the
+    /// things are lists
+    /// 
+    /// 
+    /// 
+    /// 
+    /// 
+    public static object RunFn2(Func func, object x, object y)
+    {
+        if (x is Image || y is Image)
         {
-            var fileName = Guid.NewGuid() + extension;
-            return Path.Combine(path, fileName);
+            return func(x, y);
         }
 
-        /// 
-        /// Test if an operator exists.
-        /// 
-        /// Name of the operator
-        ///  if the operator exists; otherwise, 
-        public static bool Have(string name)
+        if (x is Array || y is Array)
         {
-            return NetVips.TypeFind("VipsOperation", name) != IntPtr.Zero;
+            return ZipExpand(x, y).Select(o => func(o[0], o[1])).ToArray();
         }
+
+        return func(x, y);
     }
 
-    internal class ObjectComparerDelta : IEqualityComparer
+    /// 
+    /// run a function on an image and on a single pixel, the results
+    /// should match
+    /// 
+    /// 
+    /// 
+    /// 
+    /// 
+    public static void RunCmp(Image im, int x, int y, Func func)
     {
-        private double delta;
+        var a = im[x, y];
+        var v1 = func(a);
+        var im2 = (Image)func(im);
+        var v2 = im2[x, y];
 
-        public ObjectComparerDelta(double delta)
-        {
-            this.delta = delta;
-        }
+        AssertAlmostEqualObjects(v1 is IEnumerable enumerable ? enumerable : new[] { v1 }, v2);
+    }
 
-        public new bool Equals(object x, object y)
-        {
-            var a = Convert.ToDouble(x);
-            var b = Convert.ToDouble(y);
+    /// 
+    /// run a function on a pair of images and on a pair of pixels, the results
+    /// should match
+    /// 
+    /// 
+    /// 
+    /// 
+    /// 
+    /// 
+    /// 
+    public static void RunCmp2(Image left, Image right, int x, int y, Func func)
+    {
+        var a = left[x, y];
+        var b = right[x, y];
+        var v1 = func(a, b);
+        var after = (Image)func(left, right);
+        var v2 = after[x, y];
 
-            return Math.Abs(a - b) <= delta;
-        }
+        AssertAlmostEqualObjects(v1 is IEnumerable enumerable ? enumerable : new[] { v1 }, v2);
+    }
 
-        public int GetHashCode(object obj)
+    /// 
+    /// run a function on a pair of images
+    /// 50,50 and 10,10 should have different values on the test image
+    /// 
+    /// 
+    /// 
+    /// 
+    /// 
+    public static void RunImage2(Image left, Image right, Func func)
+    {
+        RunCmp2(left, right, 50, 50, (x, y) => RunFn2(func, x, y));
+        RunCmp2(left, right, 10, 10, (x, y) => RunFn2(func, x, y));
+    }
+
+    /// 
+    /// run a function on (image, constant), and on (constant, image).
+    /// 50,50 and 10,10 should have different values on the test image
+    /// 
+    public static void RunConst(Func func, Image im, object c)
+    {
+        RunCmp(im, 50, 50, x => RunFn2(func, x, c));
+        RunCmp(im, 50, 50, x => RunFn2(func, c, x));
+        RunCmp(im, 10, 10, x => RunFn2(func, x, c));
+        RunCmp(im, 10, 10, x => RunFn2(func, c, x));
+    }
+
+    /// 
+    /// test a pair of things which can be lists for approx. equality
+    /// 
+    /// 
+    /// 
+    /// 
+    public static void AssertAlmostEqualObjects(IEnumerable expected, IEnumerable actual, double delta = 0.0001)
+    {
+        Assert.True(expected.Cast().SequenceEqual(actual.Cast(), new ObjectComparerDelta(delta)));
+    }
+
+    /// 
+    /// test a pair of things which can be lists for difference less than a
+    /// thresholdy
+    /// 
+    /// 
+    /// 
+    /// 
+    public static void AssertLessThreshold(object a, object b, double diff)
+    {
+        foreach (var expand in ZipExpand(a, b))
         {
-            return obj.GetHashCode();
+            var x = Convert.ToDouble(expand[0]);
+            var y = Convert.ToDouble(expand[1]);
+            Assert.True(Math.Abs(x - y) < diff);
         }
     }
+
+    public static string GetTemporaryDirectory()
+    {
+        var tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+        Directory.CreateDirectory(tempDirectory);
+        return tempDirectory;
+    }
+
+    public static string GetTemporaryFile(string path, string extension = "")
+    {
+        var fileName = Guid.NewGuid() + extension;
+        return Path.Combine(path, fileName);
+    }
+
+    /// 
+    /// Test if an operator exists.
+    /// 
+    /// Name of the operator
+    ///  if the operator exists; otherwise, 
+    public static bool Have(string name)
+    {
+        return NetVips.TypeFind("VipsOperation", name) != IntPtr.Zero;
+    }
+}
+
+internal class ObjectComparerDelta : IEqualityComparer
+{
+    private double delta;
+
+    public ObjectComparerDelta(double delta)
+    {
+        this.delta = delta;
+    }
+
+    public new bool Equals(object x, object y)
+    {
+        var a = Convert.ToDouble(x);
+        var b = Convert.ToDouble(y);
+
+        return Math.Abs(a - b) <= delta;
+    }
+
+    public int GetHashCode(object obj)
+    {
+        return obj.GetHashCode();
+    }
 }
\ No newline at end of file
diff --git a/tests/NetVips.Tests/HistogramTests.cs b/tests/NetVips.Tests/HistogramTests.cs
index e2cad0d1..97813f95 100644
--- a/tests/NetVips.Tests/HistogramTests.cs
+++ b/tests/NetVips.Tests/HistogramTests.cs
@@ -1,176 +1,175 @@
-namespace NetVips.Tests
-{
-    using System;
-    using Xunit;
-    using Xunit.Abstractions;
+using System;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace NetVips.Tests;
 
-    public class HistogramTests : IClassFixture
+public class HistogramTests : IClassFixture
+{
+    public HistogramTests(TestsFixture testsFixture, ITestOutputHelper output)
     {
-        public HistogramTests(TestsFixture testsFixture, ITestOutputHelper output)
-        {
-            testsFixture.SetUpLogging(output);
-        }
+        testsFixture.SetUpLogging(output);
+    }
 
-        [Fact]
-        public void TestHistCum()
-        {
-            var im = Image.Identity();
+    [Fact]
+    public void TestHistCum()
+    {
+        var im = Image.Identity();
 
-            var sum = im.Avg() * 256;
+        var sum = im.Avg() * 256;
 
-            var cum = im.HistCum();
+        var cum = im.HistCum();
 
-            var p = cum[255, 0];
-            Assert.Equal(sum, p[0]);
-        }
+        var p = cum[255, 0];
+        Assert.Equal(sum, p[0]);
+    }
 
-        [Fact]
-        public void TestHistEqual()
-        {
-            var im = Image.NewFromFile(Helper.JpegFile);
+    [Fact]
+    public void TestHistEqual()
+    {
+        var im = Image.NewFromFile(Helper.JpegFile);
 
-            var im2 = im.HistEqual();
+        var im2 = im.HistEqual();
 
-            Assert.Equal(im.Width, im2.Width);
-            Assert.Equal(im.Height, im2.Height);
+        Assert.Equal(im.Width, im2.Width);
+        Assert.Equal(im.Height, im2.Height);
 
-            Assert.True(im.Avg() < im2.Avg());
-            Assert.True(im.Deviate() < im2.Deviate());
-        }
+        Assert.True(im.Avg() < im2.Avg());
+        Assert.True(im.Deviate() < im2.Deviate());
+    }
 
-        [Fact]
-        public void TestHistIsmonotonic()
-        {
-            var im = Image.Identity();
-            Assert.True(im.HistIsmonotonic());
-        }
+    [Fact]
+    public void TestHistIsmonotonic()
+    {
+        var im = Image.Identity();
+        Assert.True(im.HistIsmonotonic());
+    }
 
-        [Fact]
-        public void TestHistLocal()
-        {
-            var im = Image.NewFromFile(Helper.JpegFile);
+    [Fact]
+    public void TestHistLocal()
+    {
+        var im = Image.NewFromFile(Helper.JpegFile);
 
-            var im2 = im.HistLocal(10, 10);
+        var im2 = im.HistLocal(10, 10);
 
-            Assert.Equal(im.Width, im2.Width);
-            Assert.Equal(im.Height, im2.Height);
+        Assert.Equal(im.Width, im2.Width);
+        Assert.Equal(im.Height, im2.Height);
 
-            Assert.True(im.Avg() < im2.Avg());
-            Assert.True(im.Deviate() < im2.Deviate());
+        Assert.True(im.Avg() < im2.Avg());
+        Assert.True(im.Deviate() < im2.Deviate());
 
-            if (NetVips.AtLeastLibvips(8, 5))
-            {
-                var im3 = im.HistLocal(10, 10, maxSlope: 3);
-                Assert.Equal(im.Width, im3.Width);
-                Assert.Equal(im.Height, im3.Height);
+        if (NetVips.AtLeastLibvips(8, 5))
+        {
+            var im3 = im.HistLocal(10, 10, maxSlope: 3);
+            Assert.Equal(im.Width, im3.Width);
+            Assert.Equal(im.Height, im3.Height);
 
-                Assert.True(im3.Deviate() < im2.Deviate());
-            }
+            Assert.True(im3.Deviate() < im2.Deviate());
         }
+    }
 
-        [Fact]
-        public void TestHistMatch()
-        {
-            var im = Image.Identity();
-            var im2 = Image.Identity();
+    [Fact]
+    public void TestHistMatch()
+    {
+        var im = Image.Identity();
+        var im2 = Image.Identity();
 
-            var matched = im.HistMatch(im2);
+        var matched = im.HistMatch(im2);
 
-            Assert.Equal(0.0, (im - matched).Abs().Max());
-        }
+        Assert.Equal(0.0, (im - matched).Abs().Max());
+    }
 
-        [Fact]
-        public void TestHistNorm()
-        {
-            var im = Image.Identity();
-            var im2 = im.HistNorm();
-            Assert.Equal(0.0, (im - im2).Abs().Max());
-        }
+    [Fact]
+    public void TestHistNorm()
+    {
+        var im = Image.Identity();
+        var im2 = im.HistNorm();
+        Assert.Equal(0.0, (im - im2).Abs().Max());
+    }
 
-        [Fact]
-        public void TestHistPlot()
-        {
-            var im = Image.Identity();
-            var im2 = im.HistPlot();
+    [Fact]
+    public void TestHistPlot()
+    {
+        var im = Image.Identity();
+        var im2 = im.HistPlot();
 
-            Assert.Equal(256, im2.Width);
-            Assert.Equal(256, im2.Height);
-            Assert.Equal(Enums.BandFormat.Uchar, im2.Format);
-            Assert.Equal(1, im2.Bands);
-        }
+        Assert.Equal(256, im2.Width);
+        Assert.Equal(256, im2.Height);
+        Assert.Equal(Enums.BandFormat.Uchar, im2.Format);
+        Assert.Equal(1, im2.Bands);
+    }
 
-        [Fact]
-        public void TestHistMap()
-        {
-            var im = Image.Identity();
+    [Fact]
+    public void TestHistMap()
+    {
+        var im = Image.Identity();
 
-            var im2 = im.Maplut(im);
+        var im2 = im.Maplut(im);
 
-            Assert.Equal(0.0, (im - im2).Abs().Max());
-        }
+        Assert.Equal(0.0, (im - im2).Abs().Max());
+    }
 
-        [Fact]
-        public void TestPercent()
-        {
-            var im = Image.NewFromFile(Helper.JpegFile)[1];
+    [Fact]
+    public void TestPercent()
+    {
+        var im = Image.NewFromFile(Helper.JpegFile)[1];
 
-            var pc = im.Percent(90);
+        var pc = im.Percent(90);
 
-            var msk = im <= pc;
-            var nSet = (msk.Avg() * msk.Width * msk.Height) / 255.0;
-            var pcSet = 100 * nSet / (msk.Width * msk.Height);
+        var msk = im <= pc;
+        var nSet = (msk.Avg() * msk.Width * msk.Height) / 255.0;
+        var pcSet = 100 * nSet / (msk.Width * msk.Height);
 
-            Assert.True(Math.Abs(pcSet - 90) < 1);
-        }
+        Assert.True(Math.Abs(pcSet - 90) < 1);
+    }
 
-        [Fact]
-        public void TestHistEntropy()
-        {
-            var im = Image.NewFromFile(Helper.JpegFile)[1];
+    [Fact]
+    public void TestHistEntropy()
+    {
+        var im = Image.NewFromFile(Helper.JpegFile)[1];
 
-            var ent = im.HistFind().HistEntropy();
+        var ent = im.HistFind().HistEntropy();
 
-            Assert.Equal(4.37, ent, 2);
-        }
+        Assert.Equal(4.37, ent, 2);
+    }
 
-        [Fact]
-        public void TestStdif()
-        {
-            var im = Image.NewFromFile(Helper.JpegFile);
+    [Fact]
+    public void TestStdif()
+    {
+        var im = Image.NewFromFile(Helper.JpegFile);
 
-            var im2 = im.Stdif(10, 10);
+        var im2 = im.Stdif(10, 10);
 
-            Assert.Equal(im.Width, im2.Width);
-            Assert.Equal(im.Height, im2.Height);
+        Assert.Equal(im.Width, im2.Width);
+        Assert.Equal(im.Height, im2.Height);
 
-            // new mean should be closer to target mean
-            Assert.True(Math.Abs(im.Avg() - 128) > Math.Abs(im2.Avg() - 128));
-        }
+        // new mean should be closer to target mean
+        Assert.True(Math.Abs(im.Avg() - 128) > Math.Abs(im2.Avg() - 128));
+    }
 
-        [SkippableFact]
-        public void TestCase()
-        {
-            // case was added in libvips 8.9.
-            Skip.IfNot(NetVips.AtLeastLibvips(8, 9), "requires libvips >= 8.9");
-
-            var x = Image.Grey(256, 256, uchar: true);
-
-            // slice into two at 128, we should get 50% of pixels in each half
-            var index = Image.Switch(x < 128, x >= 128);
-            var y = index.Case(10, 20);
-            Assert.Equal(15, y.Avg());
-
-            // slice into four
-            index = Image.Switch(
-                x < 64,
-                x >= 64 && x < 128,
-                x >= 128 && x < 192,
-                x >= 192
-            );
-            Assert.Equal(25, index.Case(10, 20, 30, 40).Avg());
-
-            // values over N should use the last value
-            Assert.Equal(22.5, index.Case(10, 20, 30).Avg());
-        }
+    [SkippableFact]
+    public void TestCase()
+    {
+        // case was added in libvips 8.9.
+        Skip.IfNot(NetVips.AtLeastLibvips(8, 9), "requires libvips >= 8.9");
+
+        var x = Image.Grey(256, 256, uchar: true);
+
+        // slice into two at 128, we should get 50% of pixels in each half
+        var index = Image.Switch(x < 128, x >= 128);
+        var y = index.Case(10, 20);
+        Assert.Equal(15, y.Avg());
+
+        // slice into four
+        index = Image.Switch(
+            x < 64,
+            x >= 64 && x < 128,
+            x >= 128 && x < 192,
+            x >= 192
+        );
+        Assert.Equal(25, index.Case(10, 20, 30, 40).Avg());
+
+        // values over N should use the last value
+        Assert.Equal(22.5, index.Case(10, 20, 30).Avg());
     }
 }
\ No newline at end of file
diff --git a/tests/NetVips.Tests/IoFuncsTests.cs b/tests/NetVips.Tests/IoFuncsTests.cs
index 3f797228..07f15b30 100644
--- a/tests/NetVips.Tests/IoFuncsTests.cs
+++ b/tests/NetVips.Tests/IoFuncsTests.cs
@@ -1,255 +1,254 @@
-namespace NetVips.Tests
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace NetVips.Tests;
+
+public class IoFuncsTests : IClassFixture
 {
-    using System;
-    using System.Collections.Generic;
-    using System.Linq;
-    using System.Runtime.InteropServices;
-    using Xunit;
-    using Xunit.Abstractions;
-
-    public class IoFuncsTests : IClassFixture
+    public IoFuncsTests(TestsFixture testsFixture, ITestOutputHelper output)
+    {
+        testsFixture.SetUpLogging(output);
+    }
+
+    /// 
+    /// test the vips7 filename splitter ... this is very fragile and annoying
+    /// code with lots of cases
+    /// 
+    [SkippableFact]
+    public void TestSplit7()
     {
-        public IoFuncsTests(TestsFixture testsFixture, ITestOutputHelper output)
+        var ex = Record.Exception(() => NetVips.PathFilename7(""));
+        Skip.IfNot(ex == null, "vips configured with --disable-deprecated, skipping test");
+
+        string[] Split(string path)
         {
-            testsFixture.SetUpLogging(output);
+            var filename7 = NetVips.PathFilename7(path);
+            var mode7 = NetVips.PathMode7(path);
+            return new[] { filename7, mode7 };
         }
 
-        /// 
-        /// test the vips7 filename splitter ... this is very fragile and annoying
-        /// code with lots of cases
-        /// 
-        [SkippableFact]
-        public void TestSplit7()
+        var cases = new Dictionary
         {
-            var ex = Record.Exception(() => NetVips.PathFilename7(""));
-            Skip.IfNot(ex == null, "vips configured with --disable-deprecated, skipping test");
-
-            string[] Split(string path)
-            {
-                var filename7 = NetVips.PathFilename7(path);
-                var mode7 = NetVips.PathMode7(path);
-                return new[] { filename7, mode7 };
-            }
-
-            var cases = new Dictionary
             {
+                "c:\\silly:dir:name\\fr:ed.tif:jpeg:95,,,,c:\\icc\\srgb.icc",
+                new[]
                 {
-                    "c:\\silly:dir:name\\fr:ed.tif:jpeg:95,,,,c:\\icc\\srgb.icc",
-                    new[]
-                    {
-                        "c:\\silly:dir:name\\fr:ed.tif",
-                        "jpeg:95,,,,c:\\icc\\srgb.icc"
-                    }
-                },
+                    "c:\\silly:dir:name\\fr:ed.tif",
+                    "jpeg:95,,,,c:\\icc\\srgb.icc"
+                }
+            },
+            {
+                "I180:",
+                new[]
                 {
-                    "I180:",
-                    new[]
-                    {
-                        "I180",
-                        ""
-                    }
-                },
+                    "I180",
+                    ""
+                }
+            },
+            {
+                "c:\\silly:",
+                new[]
                 {
-                    "c:\\silly:",
-                    new[]
-                    {
-                        "c:\\silly",
-                        ""
-                    }
-                },
+                    "c:\\silly",
+                    ""
+                }
+            },
+            {
+                "c:\\program files\\x:hello",
+                new[]
                 {
-                    "c:\\program files\\x:hello",
-                    new[]
-                    {
-                        "c:\\program files\\x",
-                        "hello"
-                    }
-                },
+                    "c:\\program files\\x",
+                    "hello"
+                }
+            },
+            {
+                "C:\\fixtures\\2569067123_aca715a2ee_o.jpg",
+                new[]
                 {
                     "C:\\fixtures\\2569067123_aca715a2ee_o.jpg",
-                    new[]
-                    {
-                        "C:\\fixtures\\2569067123_aca715a2ee_o.jpg",
-                        ""
-                    }
+                    ""
                 }
-            };
-
-            foreach (var entry in cases)
-            {
-                Assert.Equal(entry.Value, Split(entry.Key));
             }
-        }
+        };
 
-        [Fact]
-        public void TestNewFromImage()
+        foreach (var entry in cases)
         {
-            var im = Image.MaskIdeal(100, 100, 0.5, reject: true, optical: true);
-
-            var im2 = im.NewFromImage(12);
-
-            Assert.Equal(im.Width, im2.Width);
-            Assert.Equal(im.Height, im2.Height);
-            Assert.Equal(im.Interpretation, im2.Interpretation);
-            Assert.Equal(im.Format, im2.Format);
-            Assert.Equal(im.Xres, im2.Xres);
-            Assert.Equal(im.Yres, im2.Yres);
-            Assert.Equal(im.Xoffset, im2.Xoffset);
-            Assert.Equal(im.Yoffset, im2.Yoffset);
-            Assert.Equal(1, im2.Bands);
-            Assert.Equal(12, im2.Avg());
-
-            im2 = im.NewFromImage(1, 2, 3);
-            Assert.Equal(3, im2.Bands);
-            Assert.Equal(2, im2.Avg());
+            Assert.Equal(entry.Value, Split(entry.Key));
         }
+    }
 
-        [Fact]
-        public void TestNewFromMemory()
-        {
-            var s = new byte[200];
-            var im = Image.NewFromMemory(s, 20, 10, 1, Enums.BandFormat.Uchar);
-            Assert.Equal(20, im.Width);
-            Assert.Equal(10, im.Height);
-            Assert.Equal(Enums.BandFormat.Uchar, im.Format);
-            Assert.Equal(1, im.Bands);
-            Assert.Equal(0, im.Avg());
+    [Fact]
+    public void TestNewFromImage()
+    {
+        var im = Image.MaskIdeal(100, 100, 0.5, reject: true, optical: true);
+
+        var im2 = im.NewFromImage(12);
+
+        Assert.Equal(im.Width, im2.Width);
+        Assert.Equal(im.Height, im2.Height);
+        Assert.Equal(im.Interpretation, im2.Interpretation);
+        Assert.Equal(im.Format, im2.Format);
+        Assert.Equal(im.Xres, im2.Xres);
+        Assert.Equal(im.Yres, im2.Yres);
+        Assert.Equal(im.Xoffset, im2.Xoffset);
+        Assert.Equal(im.Yoffset, im2.Yoffset);
+        Assert.Equal(1, im2.Bands);
+        Assert.Equal(12, im2.Avg());
+
+        im2 = im.NewFromImage(1, 2, 3);
+        Assert.Equal(3, im2.Bands);
+        Assert.Equal(2, im2.Avg());
+    }
 
-            im += 10;
-            Assert.Equal(10, im.Avg());
-        }
+    [Fact]
+    public void TestNewFromMemory()
+    {
+        var s = new byte[200];
+        var im = Image.NewFromMemory(s, 20, 10, 1, Enums.BandFormat.Uchar);
+        Assert.Equal(20, im.Width);
+        Assert.Equal(10, im.Height);
+        Assert.Equal(Enums.BandFormat.Uchar, im.Format);
+        Assert.Equal(1, im.Bands);
+        Assert.Equal(0, im.Avg());
+
+        im += 10;
+        Assert.Equal(10, im.Avg());
+    }
 
-        [Fact]
-        public void TestNewFromMemoryPtr()
-        {
-            const int sizeInBytes = 100;
-            var memory = Marshal.AllocHGlobal(sizeInBytes);
+    [Fact]
+    public void TestNewFromMemoryPtr()
+    {
+        const int sizeInBytes = 100;
+        var memory = Marshal.AllocHGlobal(sizeInBytes);
 
-            // Zero the memory
-            // Unsafe.InitBlockUnaligned((byte*)memory, 0, sizeInBytes);
-            Marshal.Copy(new byte[sizeInBytes], 0, memory, sizeInBytes);
+        // Zero the memory
+        // Unsafe.InitBlockUnaligned((byte*)memory, 0, sizeInBytes);
+        Marshal.Copy(new byte[sizeInBytes], 0, memory, sizeInBytes);
 
-            // Avoid reusing the image after subsequent use
-            var prevMax = Cache.Max;
-            Cache.Max = 0;
+        // Avoid reusing the image after subsequent use
+        var prevMax = Cache.Max;
+        Cache.Max = 0;
 
-            using (var im = Image.NewFromMemory(memory, sizeInBytes, 10, 10, 1, Enums.BandFormat.Uchar))
-            {
-                im.OnPostClose += () => Marshal.FreeHGlobal(memory);
+        using (var im = Image.NewFromMemory(memory, sizeInBytes, 10, 10, 1, Enums.BandFormat.Uchar))
+        {
+            im.OnPostClose += () => Marshal.FreeHGlobal(memory);
 
-                Assert.Equal(0, im.Avg());
-            } // OnPostClose
+            Assert.Equal(0, im.Avg());
+        } // OnPostClose
 
-            Cache.Max = prevMax;
-        }
+        Cache.Max = prevMax;
+    }
 
-        [SkippableFact]
-        public void TestGetFields()
-        {
-            Skip.IfNot(NetVips.AtLeastLibvips(8, 5), "requires libvips >= 8.5");
+    [SkippableFact]
+    public void TestGetFields()
+    {
+        Skip.IfNot(NetVips.AtLeastLibvips(8, 5), "requires libvips >= 8.5");
 
-            var im = Image.Black(10, 10);
-            var fields = im.GetFields();
+        var im = Image.Black(10, 10);
+        var fields = im.GetFields();
 
-            // we might add more fields later
-            Assert.True(fields.Length > 10);
+        // we might add more fields later
+        Assert.True(fields.Length > 10);
 
-            Assert.Equal("width", fields[0]);
-        }
+        Assert.Equal("width", fields[0]);
+    }
 
-        [SkippableFact]
-        public void TestGetSuffixes()
-        {
-            Skip.IfNot(NetVips.AtLeastLibvips(8, 8), "requires libvips >= 8.8");
+    [SkippableFact]
+    public void TestGetSuffixes()
+    {
+        Skip.IfNot(NetVips.AtLeastLibvips(8, 8), "requires libvips >= 8.8");
 
-            var suffixes = NetVips.GetSuffixes();
+        var suffixes = NetVips.GetSuffixes();
 
-            // vips supports these file types by default
-            // (without being dependent on external dependencies):
-            // - Native file format (`*.v`, `*.vips`).
-            // - PPM images (`*.ppm`, `*.pgm`, `*.pbm`, `*.pfm`).
-            // - Analyze images (`*.hdr`).
-            Assert.True(suffixes.Length >= 7);
-        }
+        // vips supports these file types by default
+        // (without being dependent on external dependencies):
+        // - Native file format (`*.v`, `*.vips`).
+        // - PPM images (`*.ppm`, `*.pgm`, `*.pbm`, `*.pfm`).
+        // - Analyze images (`*.hdr`).
+        Assert.True(suffixes.Length >= 7);
+    }
 
-        [Fact]
-        public void TestFindLoadUtf8()
-        {
-            Assert.Equal("VipsForeignLoadJpegFile", Image.FindLoad(Helper.JpegFile));
-        }
+    [Fact]
+    public void TestFindLoadUtf8()
+    {
+        Assert.Equal("VipsForeignLoadJpegFile", Image.FindLoad(Helper.JpegFile));
+    }
 
-        [Fact]
-        public void TestWriteToMemory()
-        {
-            var s = new byte[200];
-            var im = Image.NewFromMemory(s, 20, 10, 1, Enums.BandFormat.Uchar);
-            var t = im.WriteToMemory();
-            Assert.True(s.SequenceEqual(t));
-        }
+    [Fact]
+    public void TestWriteToMemory()
+    {
+        var s = new byte[200];
+        var im = Image.NewFromMemory(s, 20, 10, 1, Enums.BandFormat.Uchar);
+        var t = im.WriteToMemory();
+        Assert.True(s.SequenceEqual(t));
+    }
 
-        [SkippableFact]
-        public void TestRegion()
-        {
-            Skip.IfNot(NetVips.AtLeastLibvips(8, 8), "requires libvips >= 8.8");
+    [SkippableFact]
+    public void TestRegion()
+    {
+        Skip.IfNot(NetVips.AtLeastLibvips(8, 8), "requires libvips >= 8.8");
 
-            var im = Image.Black(100, 100);
-            var region = Region.New(im);
-            var data = region.Fetch(0, 0, 10, 10);
+        var im = Image.Black(100, 100);
+        var region = Region.New(im);
+        var data = region.Fetch(0, 0, 10, 10);
 
-            Assert.Equal(10, region.Width);
-            Assert.Equal(10, region.Height);
-            Assert.True(data.Length == 100);
-            Assert.True(data.All(p => p == 0));
+        Assert.Equal(10, region.Width);
+        Assert.Equal(10, region.Height);
+        Assert.True(data.Length == 100);
+        Assert.True(data.All(p => p == 0));
 
-            data = region.Fetch(0, 0, 20, 10);
+        data = region.Fetch(0, 0, 20, 10);
 
-            Assert.Equal(20, region.Width);
-            Assert.Equal(10, region.Height);
-            Assert.True(data.Length == 200);
-            Assert.True(data.All(p => p == 0));
-        }
+        Assert.Equal(20, region.Width);
+        Assert.Equal(10, region.Height);
+        Assert.True(data.Length == 200);
+        Assert.True(data.All(p => p == 0));
+    }
 
-        [Fact]
-        public void TestInvalidate()
-        {
-            byte[] data = { 0 };
+    [Fact]
+    public void TestInvalidate()
+    {
+        byte[] data = { 0 };
 
-            var im = Image.NewFromMemory(data, 1, 1, 1, Enums.BandFormat.Uchar);
-            var point = im[0, 0];
-            Assert.Equal(data[0], point[0]);
+        var im = Image.NewFromMemory(data, 1, 1, 1, Enums.BandFormat.Uchar);
+        var point = im[0, 0];
+        Assert.Equal(data[0], point[0]);
 
-            data[0] = 1;
+        data[0] = 1;
 
-            point = im[0, 0];
-            Assert.True(point[0] <= data[0]); // can be either 0 or 1
+        point = im[0, 0];
+        Assert.True(point[0] <= data[0]); // can be either 0 or 1
 
-            im.Invalidate();
-            point = im[0, 0];
-            Assert.Equal(data[0], point[0]);
-        }
+        im.Invalidate();
+        point = im[0, 0];
+        Assert.Equal(data[0], point[0]);
+    }
 
-        [SkippableFact]
-        public void TestSetProgress()
-        {
-            Skip.IfNot(Helper.Have("dzsave"), "no dzsave support, skipping test");
+    [SkippableFact]
+    public void TestSetProgress()
+    {
+        Skip.IfNot(Helper.Have("dzsave"), "no dzsave support, skipping test");
 
-            var im = Image.NewFromFile(Helper.JpegFile, access: Enums.Access.Sequential);
+        var im = Image.NewFromFile(Helper.JpegFile, access: Enums.Access.Sequential);
 
-            var lastPercent = 0;
+        var lastPercent = 0;
 
-            var progress = new Progress(percent => lastPercent = percent);
-            im.SetProgress(progress);
+        var progress = new Progress(percent => lastPercent = percent);
+        im.SetProgress(progress);
 
-            var buf = im.DzsaveBuffer("image-pyramid");
-            Assert.True(buf.Length > 0);
-            Assert.True(lastPercent <= 100);
-        }
+        var buf = im.DzsaveBuffer("image-pyramid");
+        Assert.True(buf.Length > 0);
+        Assert.True(lastPercent <= 100);
+    }
 
-        [Fact]
-        public void TestModuleInitializer()
-        {
-            // vips should have been initialized when this assembly was loaded.
-            Assert.True(ModuleInitializer.VipsInitialized);
-        }
+    [Fact]
+    public void TestModuleInitializer()
+    {
+        // vips should have been initialized when this assembly was loaded.
+        Assert.True(ModuleInitializer.VipsInitialized);
     }
 }
\ No newline at end of file
diff --git a/tests/NetVips.Tests/MorphologyTests.cs b/tests/NetVips.Tests/MorphologyTests.cs
index 778fa3c5..135e4caa 100644
--- a/tests/NetVips.Tests/MorphologyTests.cs
+++ b/tests/NetVips.Tests/MorphologyTests.cs
@@ -1,79 +1,78 @@
-namespace NetVips.Tests
-{
-    using Xunit;
-    using Xunit.Abstractions;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace NetVips.Tests;
 
-    public class MorphologyTests : IClassFixture
+public class MorphologyTests : IClassFixture
+{
+    public MorphologyTests(TestsFixture testsFixture, ITestOutputHelper output)
     {
-        public MorphologyTests(TestsFixture testsFixture, ITestOutputHelper output)
-        {
-            testsFixture.SetUpLogging(output);
-        }
+        testsFixture.SetUpLogging(output);
+    }
 
-        [Fact]
-        public void TestCountlines()
-        {
-            var im = Image.Black(100, 100);
-            im = im.Mutate(x => x.DrawLine(new double[] { 255 }, 0, 50, 100, 50));
-            var nLines = im.Countlines(Enums.Direction.Horizontal);
-            Assert.Equal(1, nLines);
-        }
+    [Fact]
+    public void TestCountlines()
+    {
+        var im = Image.Black(100, 100);
+        im = im.Mutate(x => x.DrawLine(new double[] { 255 }, 0, 50, 100, 50));
+        var nLines = im.Countlines(Enums.Direction.Horizontal);
+        Assert.Equal(1, nLines);
+    }
 
-        [Fact]
-        public void TestLabelregions()
-        {
-            var im = Image.Black(100, 100);
-            im = im.Mutate(x => x.DrawCircle(new double[] { 255 }, 50, 50, 25, fill: true));
-            var mask = im.Labelregions(out var segments);
+    [Fact]
+    public void TestLabelregions()
+    {
+        var im = Image.Black(100, 100);
+        im = im.Mutate(x => x.DrawCircle(new double[] { 255 }, 50, 50, 25, fill: true));
+        var mask = im.Labelregions(out var segments);
 
-            Assert.Equal(3, segments);
-            Assert.Equal(2, mask.Max());
-        }
+        Assert.Equal(3, segments);
+        Assert.Equal(2, mask.Max());
+    }
 
-        [Fact]
-        public void TestErode()
+    [Fact]
+    public void TestErode()
+    {
+        var im = Image.Black(100, 100);
+        im = im.Mutate(x => x.DrawCircle(new double[] { 255 }, 50, 50, 25, fill: true));
+        var im2 = im.Erode(Image.NewFromArray(new[,]
         {
-            var im = Image.Black(100, 100);
-            im = im.Mutate(x => x.DrawCircle(new double[] { 255 }, 50, 50, 25, fill: true));
-            var im2 = im.Erode(Image.NewFromArray(new[,]
-            {
-                {128, 255, 128},
-                {255, 255, 255},
-                {128, 255, 128}
-            }));
-            Assert.Equal(im.Width, im2.Width);
-            Assert.Equal(im.Height, im2.Height);
-            Assert.Equal(im.Bands, im2.Bands);
-            Assert.True(im.Avg() > im2.Avg());
-        }
+            {128, 255, 128},
+            {255, 255, 255},
+            {128, 255, 128}
+        }));
+        Assert.Equal(im.Width, im2.Width);
+        Assert.Equal(im.Height, im2.Height);
+        Assert.Equal(im.Bands, im2.Bands);
+        Assert.True(im.Avg() > im2.Avg());
+    }
 
-        [Fact]
-        public void TestDilate()
+    [Fact]
+    public void TestDilate()
+    {
+        var im = Image.Black(100, 100);
+        im = im.Mutate(x => x.DrawCircle(new double[] { 255 }, 50, 50, 25, fill: true));
+        var im2 = im.Dilate(Image.NewFromArray(new[,]
         {
-            var im = Image.Black(100, 100);
-            im = im.Mutate(x => x.DrawCircle(new double[] { 255 }, 50, 50, 25, fill: true));
-            var im2 = im.Dilate(Image.NewFromArray(new[,]
-            {
-                {128, 255, 128},
-                {255, 255, 255},
-                {128, 255, 128}
-            }));
-            Assert.Equal(im.Width, im2.Width);
-            Assert.Equal(im.Height, im2.Height);
-            Assert.Equal(im.Bands, im2.Bands);
-            Assert.True(im2.Avg() > im.Avg());
-        }
+            {128, 255, 128},
+            {255, 255, 255},
+            {128, 255, 128}
+        }));
+        Assert.Equal(im.Width, im2.Width);
+        Assert.Equal(im.Height, im2.Height);
+        Assert.Equal(im.Bands, im2.Bands);
+        Assert.True(im2.Avg() > im.Avg());
+    }
 
-        [Fact]
-        public void TestRank()
-        {
-            var im = Image.Black(100, 100);
-            im = im.Mutate(x => x.DrawCircle(new double[] { 255 }, 50, 50, 25, fill: true));
-            var im2 = im.Rank(3, 3, 8);
-            Assert.Equal(im.Width, im2.Width);
-            Assert.Equal(im.Height, im2.Height);
-            Assert.Equal(im.Bands, im2.Bands);
-            Assert.True(im2.Avg() > im.Avg());
-        }
+    [Fact]
+    public void TestRank()
+    {
+        var im = Image.Black(100, 100);
+        im = im.Mutate(x => x.DrawCircle(new double[] { 255 }, 50, 50, 25, fill: true));
+        var im2 = im.Rank(3, 3, 8);
+        Assert.Equal(im.Width, im2.Width);
+        Assert.Equal(im.Height, im2.Height);
+        Assert.Equal(im.Bands, im2.Bands);
+        Assert.True(im2.Avg() > im.Avg());
     }
 }
\ No newline at end of file
diff --git a/tests/NetVips.Tests/ResampleTests.cs b/tests/NetVips.Tests/ResampleTests.cs
index 09cd733a..401055b7 100644
--- a/tests/NetVips.Tests/ResampleTests.cs
+++ b/tests/NetVips.Tests/ResampleTests.cs
@@ -1,342 +1,341 @@
-namespace NetVips.Tests
-{
-    using System;
-    using System.IO;
-    using Xunit;
-    using Xunit.Abstractions;
+using System;
+using System.IO;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace NetVips.Tests;
 
-    public class ResampleTests : IClassFixture
+public class ResampleTests : IClassFixture
+{
+    public ResampleTests(TestsFixture testsFixture, ITestOutputHelper output)
     {
-        public ResampleTests(TestsFixture testsFixture, ITestOutputHelper output)
-        {
-            testsFixture.SetUpLogging(output);
-        }
+        testsFixture.SetUpLogging(output);
+    }
 
-        #region helpers
+    #region helpers
 
-        /// 
-        /// Run a function expecting a complex image on a two-band image
-        /// 
-        /// 
-        /// 
-        /// 
-        public Image RunCmplx(Func func, Image image)
+    /// 
+    /// Run a function expecting a complex image on a two-band image
+    /// 
+    /// 
+    /// 
+    /// 
+    public Image RunCmplx(Func func, Image image)
+    {
+        var newFormat = image.Format switch
         {
-            var newFormat = image.Format switch
-            {
-                Enums.BandFormat.Float => Enums.BandFormat.Complex,
-                Enums.BandFormat.Double => Enums.BandFormat.Dpcomplex,
-                _ => throw new Exception("run_cmplx: not float or double")
-            };
+            Enums.BandFormat.Float => Enums.BandFormat.Complex,
+            Enums.BandFormat.Double => Enums.BandFormat.Dpcomplex,
+            _ => throw new Exception("run_cmplx: not float or double")
+        };
 
-            // tag as complex, run, revert tagging
-            var cmplx = image.Copy(bands: 1, format: newFormat);
-            var cmplxResult = func(cmplx);
+        // tag as complex, run, revert tagging
+        var cmplx = image.Copy(bands: 1, format: newFormat);
+        var cmplxResult = func(cmplx);
 
-            return cmplxResult.Copy(bands: 2, format: image.Format);
-        }
+        return cmplxResult.Copy(bands: 2, format: image.Format);
+    }
 
-        /// 
-        /// Transform image coordinates to polar
-        /// 
-        /// 
-        /// The image is transformed so that it is wrapped around a point in the
-        /// centre. Vertical straight lines become circles or segments of circles,
-        /// horizontal straight lines become radial spokes.
-        /// 
-        /// 
-        /// 
-        public Image ToPolar(Image image)
+    /// 
+    /// Transform image coordinates to polar
+    /// 
+    /// 
+    /// The image is transformed so that it is wrapped around a point in the
+    /// centre. Vertical straight lines become circles or segments of circles,
+    /// horizontal straight lines become radial spokes.
+    /// 
+    /// 
+    /// 
+    public Image ToPolar(Image image)
+    {
+        // xy image, zero in the centre, scaled to fit image to a circle
+        var xy = Image.Xyz(image.Width, image.Height);
+        xy -= new[]
         {
-            // xy image, zero in the centre, scaled to fit image to a circle
-            var xy = Image.Xyz(image.Width, image.Height);
-            xy -= new[]
-            {
-                image.Width / 2.0,
-                image.Height / 2.0
-            };
-            var scale = Math.Min(image.Width, image.Height) / Convert.ToDouble(image.Width);
-            xy *= 2.0 / scale;
-
-            // to polar, scale vertical axis to 360 degrees
-            var index = RunCmplx(x => x.Polar(), xy);
-            index *= new[]
-            {
-                1,
-                image.Height / 360.0
-            };
+            image.Width / 2.0,
+            image.Height / 2.0
+        };
+        var scale = Math.Min(image.Width, image.Height) / Convert.ToDouble(image.Width);
+        xy *= 2.0 / scale;
+
+        // to polar, scale vertical axis to 360 degrees
+        var index = RunCmplx(x => x.Polar(), xy);
+        index *= new[]
+        {
+            1,
+            image.Height / 360.0
+        };
 
-            return image.Mapim(index);
-        }
+        return image.Mapim(index);
+    }
 
-        /// 
-        /// Transform image coordinates to rectangular.
-        /// 
-        /// 
-        /// The image is transformed so that it is unwrapped from a point in the
-        /// centre. Circles or segments of circles become vertical straight lines,
-        /// radial lines become horizontal lines.
-        /// 
-        /// 
-        /// 
-        public Image ToRectangular(Image image)
+    /// 
+    /// Transform image coordinates to rectangular.
+    /// 
+    /// 
+    /// The image is transformed so that it is unwrapped from a point in the
+    /// centre. Circles or segments of circles become vertical straight lines,
+    /// radial lines become horizontal lines.
+    /// 
+    /// 
+    /// 
+    public Image ToRectangular(Image image)
+    {
+        // xy image, vertical scaled to 360 degrees
+        var xy = Image.Xyz(image.Width, image.Height);
+        xy *= new[]
         {
-            // xy image, vertical scaled to 360 degrees
-            var xy = Image.Xyz(image.Width, image.Height);
-            xy *= new[]
-            {
-                1,
-                360.0 / image.Height
-            };
-
-            // to rect, scale to image rect
-            var index = RunCmplx(x => x.Rect(), xy);
-            var scale = Math.Min(image.Width, image.Height) / Convert.ToDouble(image.Width);
-            index *= scale / 2.0;
-            index += new[]
-            {
-                image.Width / 2.0,
-                image.Height / 2.0
-            };
+            1,
+            360.0 / image.Height
+        };
+
+        // to rect, scale to image rect
+        var index = RunCmplx(x => x.Rect(), xy);
+        var scale = Math.Min(image.Width, image.Height) / Convert.ToDouble(image.Width);
+        index *= scale / 2.0;
+        index += new[]
+        {
+            image.Width / 2.0,
+            image.Height / 2.0
+        };
 
-            return image.Mapim(index);
-        }
+        return image.Mapim(index);
+    }
 
-        #endregion
+    #endregion
 
-        [Fact]
-        public void TestAffine()
-        {
-            var im = Image.NewFromFile(Helper.JpegFile);
+    [Fact]
+    public void TestAffine()
+    {
+        var im = Image.NewFromFile(Helper.JpegFile);
 
-            // vsqbs is non-interpolatory, don't test this way
-            foreach (var name in new[] { "nearest", "bicubic", "bilinear", "nohalo", "lbb" })
+        // vsqbs is non-interpolatory, don't test this way
+        foreach (var name in new[] { "nearest", "bicubic", "bilinear", "nohalo", "lbb" })
+        {
+            var x = im;
+            var interpolate = Interpolate.NewFromName(name);
+            for (var i = 0; i < 4; i++)
             {
-                var x = im;
-                var interpolate = Interpolate.NewFromName(name);
-                for (var i = 0; i < 4; i++)
-                {
-                    x = x.Affine(new double[] { 0, 1, 1, 0 }, interpolate: interpolate);
-                }
-
-                Assert.Equal(0, (x - im).Abs().Max());
+                x = x.Affine(new double[] { 0, 1, 1, 0 }, interpolate: interpolate);
             }
-        }
 
-        [Fact]
-        public void TestReduce()
-        {
-            var im = Image.NewFromFile(Helper.JpegFile);
+            Assert.Equal(0, (x - im).Abs().Max());
+        }
+    }
 
-            // cast down to 0-127, the smallest range, so we aren't messed up by
-            // clipping
-            im = im.Cast(Enums.BandFormat.Char);
+    [Fact]
+    public void TestReduce()
+    {
+        var im = Image.NewFromFile(Helper.JpegFile);
 
-            foreach (var fac in new[] { 1, 1.1, 1.5, 1.999 })
-            {
-                foreach (var fmt in Helper.AllFormats)
-                {
-                    foreach (var kernel in new[]
-                    {
-                        Enums.Kernel.Nearest,
-                        Enums.Kernel.Linear,
-                        Enums.Kernel.Cubic,
-                        Enums.Kernel.Lanczos2,
-                        Enums.Kernel.Lanczos3
-                    })
-                    {
-                        var x = im.Cast(fmt);
-                        var r = x.Reduce(fac, fac, kernel: kernel);
-
-                        var d = Math.Abs(r.Avg() - im.Avg());
-                        Assert.True(d < 2);
-                    }
-                }
-            }
+        // cast down to 0-127, the smallest range, so we aren't messed up by
+        // clipping
+        im = im.Cast(Enums.BandFormat.Char);
 
-            // try constant images ... should not change the constant
-            foreach (var @const in new[] { 0, 1, 2, 254, 255 })
+        foreach (var fac in new[] { 1, 1.1, 1.5, 1.999 })
+        {
+            foreach (var fmt in Helper.AllFormats)
             {
-                im = (Image.Black(10, 10) + @const).Cast(Enums.BandFormat.Uchar);
                 foreach (var kernel in new[]
+                         {
+                             Enums.Kernel.Nearest,
+                             Enums.Kernel.Linear,
+                             Enums.Kernel.Cubic,
+                             Enums.Kernel.Lanczos2,
+                             Enums.Kernel.Lanczos3
+                         })
                 {
-                    Enums.Kernel.Nearest,
-                    Enums.Kernel.Linear,
-                    Enums.Kernel.Cubic,
-                    Enums.Kernel.Lanczos2,
-                    Enums.Kernel.Lanczos3
-                })
-                {
-                    // Console.WriteLine($"testing kernel = {kernel}");
-                    // Console.WriteLine($"testing const = {@const}");
-                    var shr = im.Reduce(2, 2, kernel: kernel);
-                    var d = Math.Abs(shr.Avg() - im.Avg());
-                    Assert.Equal(0, d);
+                    var x = im.Cast(fmt);
+                    var r = x.Reduce(fac, fac, kernel: kernel);
+
+                    var d = Math.Abs(r.Avg() - im.Avg());
+                    Assert.True(d < 2);
                 }
             }
         }
 
-        [Fact]
-        public void TestResize()
+        // try constant images ... should not change the constant
+        foreach (var @const in new[] { 0, 1, 2, 254, 255 })
         {
-            var im = Image.NewFromFile(Helper.JpegFile);
-            var im2 = im.Resize(0.25);
-            Assert.Equal(Math.Round(im.Width / 4.0), im2.Width);
-            Assert.Equal(Math.Round(im.Height / 4.0), im2.Height);
-
-            // test geometry rounding corner case
-            im = Image.Black(100, 1);
-            var x = im.Resize(0.5);
-            Assert.Equal(50, x.Width);
-            Assert.Equal(1, x.Height);
+            im = (Image.Black(10, 10) + @const).Cast(Enums.BandFormat.Uchar);
+            foreach (var kernel in new[]
+                     {
+                         Enums.Kernel.Nearest,
+                         Enums.Kernel.Linear,
+                         Enums.Kernel.Cubic,
+                         Enums.Kernel.Lanczos2,
+                         Enums.Kernel.Lanczos3
+                     })
+            {
+                // Console.WriteLine($"testing kernel = {kernel}");
+                // Console.WriteLine($"testing const = {@const}");
+                var shr = im.Reduce(2, 2, kernel: kernel);
+                var d = Math.Abs(shr.Avg() - im.Avg());
+                Assert.Equal(0, d);
+            }
         }
+    }
 
-        [Fact]
-        public void TestShrink()
-        {
-            var im = Image.NewFromFile(Helper.JpegFile);
-            var im2 = im.Shrink(4, 4);
-            Assert.Equal(Math.Round(im.Width / 4.0), im2.Width);
-            Assert.Equal(Math.Round(im.Height / 4.0), im2.Height);
-            Assert.True(Math.Abs(im.Avg() - im2.Avg()) < 1);
-
-            im2 = im.Shrink(2.5, 2.5);
-            Assert.Equal(Math.Round(im.Width / 2.5), im2.Width);
-            Assert.Equal(Math.Round(im.Height / 2.5), im2.Height);
-            Assert.True(Math.Abs(im.Avg() - im2.Avg()) < 1);
-        }
+    [Fact]
+    public void TestResize()
+    {
+        var im = Image.NewFromFile(Helper.JpegFile);
+        var im2 = im.Resize(0.25);
+        Assert.Equal(Math.Round(im.Width / 4.0), im2.Width);
+        Assert.Equal(Math.Round(im.Height / 4.0), im2.Height);
+
+        // test geometry rounding corner case
+        im = Image.Black(100, 1);
+        var x = im.Resize(0.5);
+        Assert.Equal(50, x.Width);
+        Assert.Equal(1, x.Height);
+    }
 
-        [SkippableFact]
-        public void TestThumbnail()
-        {
-            Skip.IfNot(NetVips.AtLeastLibvips(8, 5), "requires libvips >= 8.5");
+    [Fact]
+    public void TestShrink()
+    {
+        var im = Image.NewFromFile(Helper.JpegFile);
+        var im2 = im.Shrink(4, 4);
+        Assert.Equal(Math.Round(im.Width / 4.0), im2.Width);
+        Assert.Equal(Math.Round(im.Height / 4.0), im2.Height);
+        Assert.True(Math.Abs(im.Avg() - im2.Avg()) < 1);
+
+        im2 = im.Shrink(2.5, 2.5);
+        Assert.Equal(Math.Round(im.Width / 2.5), im2.Width);
+        Assert.Equal(Math.Round(im.Height / 2.5), im2.Height);
+        Assert.True(Math.Abs(im.Avg() - im2.Avg()) < 1);
+    }
 
-            var im = Image.Thumbnail(Helper.JpegFile, 100);
-            Assert.Equal(100, im.Width);
-            Assert.Equal(3, im.Bands);
+    [SkippableFact]
+    public void TestThumbnail()
+    {
+        Skip.IfNot(NetVips.AtLeastLibvips(8, 5), "requires libvips >= 8.5");
 
-            // the average shouldn't move too much
-            var imOrig = Image.NewFromFile(Helper.JpegFile);
-            Assert.True(Math.Abs(imOrig.Avg() - im.Avg()) < 1);
+        var im = Image.Thumbnail(Helper.JpegFile, 100);
+        Assert.Equal(100, im.Width);
+        Assert.Equal(3, im.Bands);
 
-            // make sure we always get the right width
-            for (var width = 1000; width >= 1; width -= 13)
-            {
-                im = Image.Thumbnail(Helper.JpegFile, width);
-                Assert.Equal(width, im.Width);
-            }
+        // the average shouldn't move too much
+        var imOrig = Image.NewFromFile(Helper.JpegFile);
+        Assert.True(Math.Abs(imOrig.Avg() - im.Avg()) < 1);
 
-            // should fit one of width or height
-            im = Image.Thumbnail(Helper.JpegFile, 100, height: 300);
-            Assert.Equal(100, im.Width);
-            Assert.NotEqual(300, im.Height);
-            im = Image.Thumbnail(Helper.JpegFile, 300, height: 100);
-            Assert.NotEqual(300, im.Width);
-            Assert.Equal(100, im.Height);
+        // make sure we always get the right width
+        for (var width = 1000; width >= 1; width -= 13)
+        {
+            im = Image.Thumbnail(Helper.JpegFile, width);
+            Assert.Equal(width, im.Width);
+        }
 
-            // with @crop, should fit both width and height
-            im = Image.Thumbnail(Helper.JpegFile, 100, height: 300, crop: Enums.Interesting.Centre);
+        // should fit one of width or height
+        im = Image.Thumbnail(Helper.JpegFile, 100, height: 300);
+        Assert.Equal(100, im.Width);
+        Assert.NotEqual(300, im.Height);
+        im = Image.Thumbnail(Helper.JpegFile, 300, height: 100);
+        Assert.NotEqual(300, im.Width);
+        Assert.Equal(100, im.Height);
+
+        // with @crop, should fit both width and height
+        im = Image.Thumbnail(Helper.JpegFile, 100, height: 300, crop: Enums.Interesting.Centre);
+        Assert.Equal(100, im.Width);
+        Assert.Equal(300, im.Height);
+
+        var im1 = Image.Thumbnail(Helper.JpegFile, 100);
+        var buf = File.ReadAllBytes(Helper.JpegFile);
+        var im2 = Image.ThumbnailBuffer(buf, 100);
+        Assert.True(Math.Abs(im1.Avg() - im2.Avg()) < 1);
+
+        // OME-TIFF subifd thumbnail support added in 8.10
+        if (NetVips.AtLeastLibvips(8, 10))
+        {
+            // should be able to thumbnail many-page tiff
+            im = Image.Thumbnail(Helper.OmeFile, 100);
             Assert.Equal(100, im.Width);
-            Assert.Equal(300, im.Height);
+            Assert.Equal(38, im.Height);
 
-            var im1 = Image.Thumbnail(Helper.JpegFile, 100);
-            var buf = File.ReadAllBytes(Helper.JpegFile);
-            var im2 = Image.ThumbnailBuffer(buf, 100);
-            Assert.True(Math.Abs(im1.Avg() - im2.Avg()) < 1);
+            // should be able to thumbnail individual pages from many-page tiff
+            im = Image.Thumbnail(Helper.OmeFile + "[page=0]", 100);
+            Assert.Equal(100, im.Width);
+            Assert.Equal(38, im.Height);
+            im2 = Image.Thumbnail(Helper.OmeFile + "[page=1]", 100);
+            Assert.Equal(100, im2.Width);
+            Assert.Equal(38, im2.Height);
+            Assert.True((im - im2).Abs().Max() != 0);
+
+            // should be able to thumbnail entire many-page tiff as a toilet-roll
+            // image
+            im = Image.Thumbnail(Helper.OmeFile + "[n=-1]", 100);
+            Assert.Equal(100, im.Width);
+            Assert.Equal(570, im.Height);
 
-            // OME-TIFF subifd thumbnail support added in 8.10
-            if (NetVips.AtLeastLibvips(8, 10))
+            if (Helper.Have("heifload"))
             {
-                // should be able to thumbnail many-page tiff
-                im = Image.Thumbnail(Helper.OmeFile, 100);
-                Assert.Equal(100, im.Width);
-                Assert.Equal(38, im.Height);
-
-                // should be able to thumbnail individual pages from many-page tiff
-                im = Image.Thumbnail(Helper.OmeFile + "[page=0]", 100);
-                Assert.Equal(100, im.Width);
-                Assert.Equal(38, im.Height);
-                im2 = Image.Thumbnail(Helper.OmeFile + "[page=1]", 100);
-                Assert.Equal(100, im2.Width);
-                Assert.Equal(38, im2.Height);
-                Assert.True((im - im2).Abs().Max() != 0);
-
-                // should be able to thumbnail entire many-page tiff as a toilet-roll
-                // image
-                im = Image.Thumbnail(Helper.OmeFile + "[n=-1]", 100);
-                Assert.Equal(100, im.Width);
-                Assert.Equal(570, im.Height);
-
-                if (Helper.Have("heifload"))
-                {
-                    // this image is orientation 6 ... thumbnail should flip it
-                    var thumb = Image.Thumbnail(Helper.AvifFile, 100);
+                // this image is orientation 6 ... thumbnail should flip it
+                var thumb = Image.Thumbnail(Helper.AvifFile, 100);
 
-                    // thumb should be portrait
-                    Assert.True(thumb.Width < thumb.Height);
-                    Assert.Equal(100, thumb.Height);
-                }
+                // thumb should be portrait
+                Assert.True(thumb.Width < thumb.Height);
+                Assert.Equal(100, thumb.Height);
             }
         }
+    }
 
-        [Fact]
-        public void TestSimilarity()
-        {
-            var im = Image.NewFromFile(Helper.JpegFile);
-            var im2 = im.Similarity(angle: 90);
-            var im3 = im.Affine(new double[] { 0, -1, 1, 0 });
+    [Fact]
+    public void TestSimilarity()
+    {
+        var im = Image.NewFromFile(Helper.JpegFile);
+        var im2 = im.Similarity(angle: 90);
+        var im3 = im.Affine(new double[] { 0, -1, 1, 0 });
 
-            // rounding in calculating the affine transform from the angle stops
-            // this being exactly true
-            Assert.True((im2 - im3).Abs().Max() < 50);
-        }
+        // rounding in calculating the affine transform from the angle stops
+        // this being exactly true
+        Assert.True((im2 - im3).Abs().Max() < 50);
+    }
 
-        [Fact]
-        public void TestSimilarityScale()
-        {
-            var im = Image.NewFromFile(Helper.JpegFile);
-            var im2 = im.Similarity(scale: 2);
-            var im3 = im.Affine(new double[] { 2, 0, 0, 2 });
-            Assert.Equal(0, (im2 - im3).Abs().Max());
-        }
+    [Fact]
+    public void TestSimilarityScale()
+    {
+        var im = Image.NewFromFile(Helper.JpegFile);
+        var im2 = im.Similarity(scale: 2);
+        var im3 = im.Affine(new double[] { 2, 0, 0, 2 });
+        Assert.Equal(0, (im2 - im3).Abs().Max());
+    }
 
-        [SkippableFact]
-        public void TestRotate()
-        {
-            // added in 8.7
-            Skip.IfNot(Helper.Have("rotate"), "no rotate support in this vips, skipping test");
-
-            var im = Image.NewFromFile(Helper.JpegFile);
-            var im2 = im.Rotate(90);
-            var im3 = im.Affine(new double[] { 0, -1, 1, 0 });
-            // rounding in calculating the affine transform from the angle stops
-            // this being exactly true
-            Assert.True((im2 - im3).Abs().Max() < 50);
-        }
+    [SkippableFact]
+    public void TestRotate()
+    {
+        // added in 8.7
+        Skip.IfNot(Helper.Have("rotate"), "no rotate support in this vips, skipping test");
+
+        var im = Image.NewFromFile(Helper.JpegFile);
+        var im2 = im.Rotate(90);
+        var im3 = im.Affine(new double[] { 0, -1, 1, 0 });
+        // rounding in calculating the affine transform from the angle stops
+        // this being exactly true
+        Assert.True((im2 - im3).Abs().Max() < 50);
+    }
 
-        [Fact]
-        public void TestMapim()
-        {
-            var im = Image.NewFromFile(Helper.JpegFile);
+    [Fact]
+    public void TestMapim()
+    {
+        var im = Image.NewFromFile(Helper.JpegFile);
 
-            var p = ToPolar(im);
-            var r = ToRectangular(p);
+        var p = ToPolar(im);
+        var r = ToRectangular(p);
 
-            // the left edge (which is squashed to the origin) will be badly
-            // distorted, but the rest should not be too bad
-            var a = r.Crop(50, 0, im.Width - 50, im.Height).Gaussblur(2);
-            var b = im.Crop(50, 0, im.Width - 50, im.Height).Gaussblur(2);
-            Assert.True((a - b).Abs().Max() < 20);
+        // the left edge (which is squashed to the origin) will be badly
+        // distorted, but the rest should not be too bad
+        var a = r.Crop(50, 0, im.Width - 50, im.Height).Gaussblur(2);
+        var b = im.Crop(50, 0, im.Width - 50, im.Height).Gaussblur(2);
+        Assert.True((a - b).Abs().Max() < 20);
 
-            // this was a bug at one point, strangely, if executed with debug
-            // enabled
-            // fixed in 8.7.3
-            if (NetVips.AtLeastLibvips(8, 7, 3))
-            {
-                var mp = Image.Xyz(im.Width, im.Height);
-                var interp = Interpolate.NewFromName("bicubic");
-                Assert.Equal(im.Avg(), im.Mapim(mp, interp).Avg());
-            }
+        // this was a bug at one point, strangely, if executed with debug
+        // enabled
+        // fixed in 8.7.3
+        if (NetVips.AtLeastLibvips(8, 7, 3))
+        {
+            var mp = Image.Xyz(im.Width, im.Height);
+            var interp = Interpolate.NewFromName("bicubic");
+            Assert.Equal(im.Avg(), im.Mapim(mp, interp).Avg());
         }
     }
 }
\ No newline at end of file
diff --git a/tests/NetVips.Tests/TestsFixture.cs b/tests/NetVips.Tests/TestsFixture.cs
index f04533db..025c3eae 100644
--- a/tests/NetVips.Tests/TestsFixture.cs
+++ b/tests/NetVips.Tests/TestsFixture.cs
@@ -1,28 +1,27 @@
-namespace NetVips.Tests
+using System;
+using Xunit.Abstractions;
+
+namespace NetVips.Tests;
+
+public class TestsFixture : IDisposable
 {
-    using System;
-    using Xunit.Abstractions;
+    private uint _handlerId;
 
-    public class TestsFixture : IDisposable
+    public void SetUpLogging(ITestOutputHelper output)
     {
-        private uint _handlerId;
-
-        public void SetUpLogging(ITestOutputHelper output)
+        _handlerId = Log.SetLogHandler("VIPS", Enums.LogLevelFlags.Error, (domain, level, message) =>
         {
-            _handlerId = Log.SetLogHandler("VIPS", Enums.LogLevelFlags.Error, (domain, level, message) =>
-            {
-                output.WriteLine("Domain: '{0}' Level: {1}", domain, level);
-                output.WriteLine("Message: {0}", message);
-            });
-        }
+            output.WriteLine("Domain: '{0}' Level: {1}", domain, level);
+            output.WriteLine("Message: {0}", message);
+        });
+    }
 
-        public void Dispose()
+    public void Dispose()
+    {
+        if (_handlerId > 0)
         {
-            if (_handlerId > 0)
-            {
-                Log.RemoveLogHandler("VIPS", _handlerId);
-                _handlerId = 0;
-            }
+            Log.RemoveLogHandler("VIPS", _handlerId);
+            _handlerId = 0;
         }
     }
 }
\ No newline at end of file