/// <summary> /// Set the type and value of an item of metadata. /// </summary> /// <remarks> /// Sets the type and value of an item of metadata. Any old item of the /// same name is removed. See <see cref="GValue"/> for types. /// </remarks> /// <param name="gtype">The GType of the metadata item to create.</param> /// <param name="name">The name of the piece of metadata to create.</param> /// <param name="value">The value to set as a C# value. It is /// converted to the GType, if possible.</param> public new void Set(IntPtr gtype, string name, object value) { using var gv = new GValue(); gv.SetType(gtype); gv.Set(value); VipsImage.Set(this, name, in gv.Struct); }
/// <summary> /// Set a GObject property. The value is converted to the property type, if possible. /// </summary> /// <param name="name"></param> /// <param name="value"></param> /// <returns></returns> public virtual void Set(string name, object value) { // logger.Debug($"Set: name = {name}, value = {value}"); var gtype = GetTypeOf(name); var gv = new GValue(); gv.SetType(gtype); gv.Set(value); Internal.GObject.GObjectSetProperty(this, name, ref gv.Struct); }
/// <summary> /// Set a GObject property. The value is converted to the property type, if possible. /// </summary> /// <param name="name">The name of the property to set.</param> /// <param name="value">The value.</param> /// <param name="gtype">The GType of the property.</param> public virtual void Set(IntPtr gtype, string name, object value) { // logger.Debug($"Set: gtype = {gtype}, name = {name}, value = {value}"); using (var gv = new GValue()) { gv.SetType(gtype); gv.Set(value); Internal.GObject.SetProperty(this, name, in gv.Struct); } }
/// <summary> /// Get a GObject property. /// </summary> /// <remarks> /// The value of the property is converted to a C# value. /// </remarks> /// <param name="name"></param> /// <returns></returns> public virtual object Get(string name) { // logger.Debug($"Get: name = {name}"); var pspec = GetPspec(name); if (!pspec.HasValue) { throw new VipsException("Property not found."); } var gtype = pspec.Value.ValueType; var gv = new GValue(); gv.SetType(gtype); // this will add a ref for GObject properties, that ref will be // unreffed when the gvalue is finalized Internal.GObject.GObjectGetProperty(this, name, ref gv.Struct); return(gv.Get()); }
/// <summary> /// Generate the `Image.Generated.cs` file. /// </summary> /// <remarks> /// This is used to generate the `Image.Generated.cs` file (<see cref="Image"/>). /// Use it with something like: /// <code language="lang-csharp"> /// File.WriteAllText("Image.Generated.cs", Operation.GenerateImageClass()); /// </code> /// </remarks> /// <returns></returns> public static string GenerateImageClass(string indent = " ") { // generate list of all nicknames we can generate docstrings for var allNickNames = new List <string>(); IntPtr TypeMap(ulong type, IntPtr a, IntPtr b) { AddNickname(type, allNickNames); return(IntPtr.Zero); } Base.TypeMap(Base.TypeFromName("VipsOperation"), TypeMap); // Sort allNickNames.Sort(); // Filter duplicates allNickNames = allNickNames.Distinct().ToList(); // remove operations we have to wrap by hand var exclude = new[] { "scale", "ifthenelse", "bandjoin", "bandrank", "composite" }; allNickNames = allNickNames.Where(x => !exclude.Contains(x)).ToList(); const string preamble = @"//------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. // libvips version: {0} // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------"; var stringBuilder = new StringBuilder(string.Format(preamble, $"{Base.Version(0)}.{Base.Version(1)}.{Base.Version(2)}")); stringBuilder.AppendLine() .AppendLine() .AppendLine("namespace NetVips") .AppendLine("{") .AppendLine(" public sealed partial class Image") .AppendLine(" {") .AppendLine($"{indent}#region auto-generated functions") .AppendLine(); foreach (var nickname in allNickNames) { try { stringBuilder.AppendLine(GenerateFunction(nickname, indent)); } catch (Exception) { // ignore } } 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) { var type = GValue.GTypeToCSharp(tmpFile.GetTypeOf(property)); stringBuilder.AppendLine($"{indent}/// <summary>") .AppendLine($"{indent}/// {tmpFile.GetBlurb(property)}") .AppendLine($"{indent}/// </summary>") .AppendLine($"{indent}public {type} {property.ToPascalCase()} => ({type})Get(\"{property}\");") .AppendLine(); } stringBuilder.AppendLine($"{indent}#endregion") .AppendLine(" }") .AppendLine("}"); return(stringBuilder.ToString()); }
/// <summary> /// Make a C#-style docstring + function. /// </summary> /// <remarks> /// This is used to generate the functions in <see cref="Image"/>. /// </remarks> /// <param name="operationName"></param> /// <param name="indent"></param> /// <param name="outParameters"></param> /// <returns></returns> private static string GenerateFunction(string operationName, string indent = " ", string[] outParameters = null) { var op = NewFromName(operationName); if ((op.GetFlags() & Internal.Enums.VipsOperationFlags.VIPS_OPERATION_DEPRECATED) != 0) { throw new Exception($"No such operator. Operator \"{operationName}\" is deprecated"); } // we are only interested in non-deprecated args var arguments = op.GetArgs() .Where(x => (x.Value & Internal.Enums.VipsArgumentFlags.VIPS_ARGUMENT_DEPRECATED) == 0) .ToList(); // find the first required input image arg, if any ... that will be self string memberX = null; foreach (var entry in arguments) { var name = entry.Key; var flag = entry.Value; if ((flag & Internal.Enums.VipsArgumentFlags.VIPS_ARGUMENT_INPUT) != 0 && (flag & Internal.Enums.VipsArgumentFlags.VIPS_ARGUMENT_REQUIRED) != 0 && op.GetTypeOf(name) == GValue.ImageType) { memberX = name; break; } } string[] reservedKeywords = { "in", "ref", "out", "ushort" }; var requiredInput = arguments.Where(x => (x.Value & Internal.Enums.VipsArgumentFlags.VIPS_ARGUMENT_INPUT) != 0 && (x.Value & Internal.Enums.VipsArgumentFlags.VIPS_ARGUMENT_REQUIRED) != 0 && x.Key != memberX) .Select(x => x.Key) .ToArray(); var optionalInput = arguments.Where(x => (x.Value & Internal.Enums.VipsArgumentFlags.VIPS_ARGUMENT_INPUT) != 0 && (x.Value & Internal.Enums.VipsArgumentFlags.VIPS_ARGUMENT_REQUIRED) == 0) .Select(x => x.Key) .ToArray(); var requiredOutput = arguments.Where(x => (x.Value & Internal.Enums.VipsArgumentFlags.VIPS_ARGUMENT_OUTPUT) != 0 && (x.Value & Internal.Enums.VipsArgumentFlags.VIPS_ARGUMENT_REQUIRED) != 0 || (x.Value & Internal.Enums.VipsArgumentFlags.VIPS_ARGUMENT_INPUT) != 0 && (x.Value & Internal.Enums.VipsArgumentFlags.VIPS_ARGUMENT_REQUIRED) != 0 && (x.Value & Internal.Enums.VipsArgumentFlags.VIPS_ARGUMENT_MODIFY) != 0) .Select(x => x.Key) .ToArray(); string SafeIdentifier(string name) => reservedKeywords.Contains(name) ? "@" + name : name; string SafeCast(string type, string name = "result") { switch (type) { case "GObject": case "Image": case "int[]": case "double[]": case "byte[]": case "Image[]": case "object[]": return($" as {type};"); case "int": case "double": return($" is {type} {name} ? {name} : 0;"); case "bool": return($" is {type} {name} && {name};"); case "string": return($" is {type} {name} ? {name} : null;"); default: return(";"); } } string ToNullable(string type, string name) { switch (type) { case "Image[]": case "object[]": case "int[]": case "double[]": case "byte[]": case "GObject": case "Image": case "string": return($"{type} {name} = null"); case "int": case "double": case "bool": return($"{type}? {name} = null"); default: throw new Exception("Unsupported type: " + type); } } string CheckNullable(string type, string name) { switch (type) { case "Image[]": case "object[]": case "int[]": case "double[]": case "byte[]": return($"{name} != null && {name}.Length > 0"); case "GObject": case "string": return($"{name} != null"); case "int": case "double": case "bool": return($"{name}.HasValue"); case "Image": return($"!({name} is null)"); default: throw new Exception("Unsupported type: " + type); } } var result = new StringBuilder($"{indent}/// <summary>\n"); var newOperationName = operationName.ToPascalCase(); var description = op.GetDescription(); result.AppendLine($"{indent}/// {description.FirstLetterToUpper()}") .AppendLine($"{indent}/// </summary>") .AppendLine($"{indent}/// <example>") .AppendLine($"{indent}/// <code language=\"lang-csharp\">") .Append($"{indent}/// "); if (requiredOutput.Length == 1) { var name = requiredOutput[0]; result.Append( $"{GValue.GTypeToCSharp(op.GetTypeOf(name))} {SafeIdentifier(name).ToPascalCase().FirstLetterToLower()} = "); } else if (requiredOutput.Length > 1) { result.Append("var output = "); } result.Append(memberX ?? "NetVips.Image") .Append( $".{newOperationName}({string.Join(", ", requiredInput.Select(x => SafeIdentifier(x).ToPascalCase().FirstLetterToLower()).ToArray())}"); if (outParameters != null) { if (requiredInput.Length > 0) { result.Append(", "); } result.Append( $"{string.Join(", ", outParameters.Select(name => $"out var {SafeIdentifier(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 optionalName = optionalInput[i]; result.Append( $"{SafeIdentifier(optionalName).ToPascalCase().FirstLetterToLower()}: {GValue.GTypeToCSharp(op.GetTypeOf(optionalName))}") .Append(i != optionalInput.Length - 1 ? ", " : ""); } } result.AppendLine(");"); result.AppendLine($"{indent}/// </code>") .AppendLine($"{indent}/// </example>"); foreach (var requiredName in requiredInput) { result.AppendLine( $"{indent}/// <param name=\"{requiredName.ToPascalCase().FirstLetterToLower()}\">{op.GetBlurb(requiredName)}</param>"); } if (outParameters != null) { foreach (var outParameter in outParameters) { result.AppendLine( $"{indent}/// <param name=\"{outParameter.ToPascalCase().FirstLetterToLower()}\">{op.GetBlurb(outParameter)}</param>"); } } foreach (var optionalName in optionalInput) { result.AppendLine( $"{indent}/// <param name=\"{optionalName.ToPascalCase().FirstLetterToLower()}\">{op.GetBlurb(optionalName)}</param>"); } string outputType; var outputTypes = requiredOutput.Select(name => GValue.GTypeToCSharp(op.GetTypeOf(name))).ToArray(); if (outputTypes.Length == 1) { outputType = outputTypes[0]; } else if (outputTypes.Length == 0) { outputType = "void"; } else if (outputTypes.Any(o => o != outputTypes[0])) { outputType = $"{outputTypes[0]}[]"; } else { outputType = "object[]"; } string ToCref(string name) => name.Equals("Image") || name.Equals("GObject") ? $"new <see cref=\"{name}\"/>" : name; if (outputType.EndsWith("[]")) { result.AppendLine( $"{indent}/// <returns>An array of {ToCref(outputType.Remove(outputType.Length - 2))}s</returns>"); } else if (!outputType.Equals("void")) { result.AppendLine($"{indent}/// <returns>A {ToCref(outputType)}</returns>"); } result.Append($"{indent}public ") .Append(memberX == null ? "static " : "") .Append(outputType) .Append( $" {newOperationName}({string.Join(", ", requiredInput.Select(name => $"{GValue.GTypeToCSharp(op.GetTypeOf(name))} {SafeIdentifier(name).ToPascalCase().FirstLetterToLower()}").ToArray())}"); if (outParameters != null) { if (requiredInput.Length > 0) { result.Append(", "); } result.Append(string.Join(", ", outParameters.Select(name => $"out {GValue.GTypeToCSharp(op.GetTypeOf(name))} {SafeIdentifier(name).ToPascalCase().FirstLetterToLower()}") .ToArray())); } if (optionalInput.Length > 0) { if (requiredInput.Length > 0 || outParameters != null) { result.Append(", "); } result.Append(string.Join(", ", optionalInput.Select(name => $"{ToNullable(GValue.GTypeToCSharp(op.GetTypeOf(name)), SafeIdentifier(name).ToPascalCase().FirstLetterToLower())}") .ToArray())); } result.AppendLine(")") .AppendLine($"{indent}{{"); if (optionalInput.Length > 0) { result.AppendLine($"{indent} var options = new VOption();").AppendLine(); foreach (var optionalName in optionalInput) { var safeIdentifier = SafeIdentifier(optionalName).ToPascalCase().FirstLetterToLower(); result.Append($"{indent} if (") .Append(CheckNullable(GValue.GTypeToCSharp(op.GetTypeOf(optionalName)), safeIdentifier)) .AppendLine(")") .AppendLine($"{indent} {{") .AppendLine($"{indent} options.Add(\"{optionalName}\", {safeIdentifier});") .AppendLine($"{indent} }}") .AppendLine(); } } if (outParameters != null) { if (optionalInput.Length > 0) { foreach (var outParameterName in outParameters) { result.AppendLine($"{indent} options.Add(\"{outParameterName}\", true);"); } } else { result.AppendLine($"{indent} var optionalOutput = new VOption") .AppendLine($"{indent} {{"); for (var i = 0; i < outParameters.Length; i++) { var outParameterName = outParameters[i]; result.Append($"{indent} {{\"{outParameterName}\", true}}") .AppendLine(i != outParameters.Length - 1 ? "," : ""); } result.AppendLine($"{indent} }};"); } result.AppendLine() .Append($"{indent} var results = ") .Append(memberX == null ? "Operation" : "this") .Append($".Call(\"{operationName}\"") .Append(optionalInput.Length > 0 ? ", options" : ", optionalOutput"); } else { result.Append($"{indent} "); if (outputType != "void") { result.Append("return "); } result.Append(memberX == null ? "Operation" : "this") .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 && GValue.GTypeToCSharp(op.GetTypeOf(requiredInput[0])).Equals("Image[]"); if (needToWrap) { result.Append("new object[] {"); } result.Append(string.Join(", ", requiredInput.Select(x => SafeIdentifier(x).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) { result.AppendLine() .AppendLine($"{indent} var opts = results?[1] as VOption;"); for (var i = 0; i < outParameters.Length; i++) { var outParameter = outParameters[i]; result.Append( $"{indent} {SafeIdentifier(outParameter).ToPascalCase().FirstLetterToLower()} = opts?[\"{outParameter}\"]") .AppendLine(SafeCast(GValue.GTypeToCSharp(op.GetTypeOf(outParameter)), $"out{i + 1}")); } if (outputType != "void") { result.AppendLine() .Append($"{indent} return finalResult;") .AppendLine(); } } result.Append($"{indent}}}") .AppendLine(); var optionalOutput = arguments.Where(x => (x.Value & Internal.Enums.VipsArgumentFlags.VIPS_ARGUMENT_OUTPUT) != 0 && (x.Value & Internal.Enums.VipsArgumentFlags.VIPS_ARGUMENT_REQUIRED) == 0) .Select(x => x.Key) .ToArray(); // Create method overloading if necessary if (optionalOutput.Length > 0 && outParameters == null) { result.AppendLine() .Append(GenerateFunction(operationName, indent, new[] { optionalOutput[0] })); } else if (outParameters != null && outParameters.Length != optionalOutput.Length) { result.AppendLine() .Append(GenerateFunction(operationName, indent, optionalOutput.Take(outParameters.Length + 1).ToArray())); } return(result.ToString()); }