using System; using NetVips.Internal; namespace NetVips; /// /// Wrap a object. /// public class Operation : VipsObject { /// private Operation(nint pointer) : base(pointer) { } /// /// 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) { var vop = VipsOperation.New(operationName); if (vop == IntPtr.Zero) { throw new VipsException($"no such operation {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) { if (gtype == GValue.ImageType) { value = Image.Imageize(matchImage, value); } 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; } } 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. /// 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); 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"); } if (!intro.Mutable && matchImage is MutableImage) { throw new VipsException($"unable to call {operationName}: operation must be mutable"); } 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 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); } for (var i = 0; i < intro.RequiredInput.Count; i++) { var arg = intro.RequiredInput[i]; op.Set(arg.Type, matchImage, arg.Name, args[i]); } // set all optional inputs, if any if (kwargs != null) { foreach (var item in kwargs) { 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}"); } } } // 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)) { // get all required results for (var i = 0; i < intro.RequiredOutput.Count; i++) { var arg = intro.RequiredOutput[i]; ref var result = ref results[i]; result = op.Get(arg.Name); } // fetch optional output args, if any if (kwargs != null) { var optionalArgs = new VOption(); foreach (var item in kwargs) { var name = item.Key; if (intro.OptionalOutput.ContainsKey(name)) { optionalArgs[name] = op.Get(name); } } if (optionalArgs.Count > 0) { var resultsLength = results.Length; Array.Resize(ref results, resultsLength + 1); results[resultsLength] = optionalArgs; } } Internal.VipsObject.UnrefOutputs(op); } 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); } }