using System;
using NetVips.Internal;
namespace NetVips;
///
/// Wrap a object.
///
public class Operation : VipsObject
{
///
internal 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);
}
}