diff --git a/Project-Aurora/AuroraCommon/Utils/CommonColorUtils.cs b/Project-Aurora/AuroraCommon/Utils/CommonColorUtils.cs index c9622060a..b2351c5aa 100644 --- a/Project-Aurora/AuroraCommon/Utils/CommonColorUtils.cs +++ b/Project-Aurora/AuroraCommon/Utils/CommonColorUtils.cs @@ -103,7 +103,7 @@ public static Color AddColors(in Color background, in Color foreground) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref Color AddColors(in Color background, in Color foreground, ref Color resultCache) + public static ref Color AddColors(ref readonly Color background, ref readonly Color foreground, ref Color resultCache) { var foreA = foreground.A; var backA = background.A; @@ -347,7 +347,6 @@ public static Color FastColorTransparent(byte r, byte g, byte b) return FastColor((byte)(r * normalizer), (byte)(g * normalizer), (byte)(b * normalizer), brightness); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Color FastColor(byte r, byte g, byte b, byte a = 255) { return Color.FromArgb( diff --git a/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/AuroraSourceGenerator.csproj b/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/AuroraSourceGenerator.csproj index 4a7d619e8..1164d6609 100644 --- a/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/AuroraSourceGenerator.csproj +++ b/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/AuroraSourceGenerator.csproj @@ -16,12 +16,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + diff --git a/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/ClassUtils.cs b/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/ClassUtils.cs new file mode 100644 index 000000000..fcd962c3f --- /dev/null +++ b/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/ClassUtils.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using Microsoft.CodeAnalysis; + +namespace AuroraSourceGenerator; + +public static class ClassUtils +{ + public static IEnumerable GetBaseTypes(INamedTypeSymbol type) + { + var currentType = type; + while (currentType != null) + { + yield return currentType; + currentType = currentType.BaseType; + } + } + + public static IEnumerable GetBaseTypes(ITypeSymbol type) + { + var currentType = type; + while (currentType != null) + { + yield return currentType; + currentType = currentType.BaseType; + } + } + + public static IEnumerable GetAllInterfaces(ITypeSymbol type) + { + return type.AllInterfaces; + } +} \ No newline at end of file diff --git a/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/IsExternalInit.cs b/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/IsExternalInit.cs new file mode 100644 index 000000000..c9526624f --- /dev/null +++ b/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/IsExternalInit.cs @@ -0,0 +1,3 @@ +namespace System.Runtime.CompilerServices; + +public class IsExternalInit; \ No newline at end of file diff --git a/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/NodePropertySourceGenerator.cs b/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/NodePropertySourceGenerator.cs index 84be5c69b..2c736f858 100644 --- a/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/NodePropertySourceGenerator.cs +++ b/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/NodePropertySourceGenerator.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Text; using Microsoft.CodeAnalysis; @@ -15,71 +16,89 @@ internal class PropertyAccess(string gsiPath, string accessPath) public string AccessPath { get; } = accessPath; } -/// -/// A sample source generator that creates C# classes based on the text file (in this case, Domain Driven Design ubiquitous language registry). -/// When using a simple text file as a baseline, we can create a non-incremental source generator. -/// [Generator] -public class NodePropertySourceGenerator : ISourceGenerator +public class NodePropertySourceGenerator : IIncrementalGenerator { - public void Initialize(GeneratorInitializationContext context) + private const string GameStateInterface = "AuroraRgb.Profiles.GameState"; + + public void Initialize(IncrementalGeneratorInitializationContext context) { - // No initialization required for this example + // Get all class declarations from syntax + IncrementalValuesProvider classDeclarations = context.SyntaxProvider + .CreateSyntaxProvider( + predicate: static (s, _) => IsSyntaxTargetForGeneration(s), + transform: static (ctx, _) => GetSemanticTargetForGeneration(ctx)) + .Where(static m => m is not null)!; + + // Combine the compilation and the class declarations + IncrementalValueProvider<(Compilation Compilation, ImmutableArray Classifications)> compilationAndClasses + = context.CompilationProvider.Combine(classDeclarations.Collect()); + + // Generate the source + context.RegisterSourceOutput(compilationAndClasses, + static (spc, source) => Execute(source.Compilation, source.Classifications, spc)); } - public void Execute(GeneratorExecutionContext context) + private static bool IsSyntaxTargetForGeneration(SyntaxNode node) + => node is ClassDeclarationSyntax classDeclaration + && classDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword); + + private static ClassDeclarationSyntax? GetSemanticTargetForGeneration(GeneratorSyntaxContext context) { - const string gamestate = "AuroraRgb.Profiles.GameState"; - HashSet ignore = [gamestate]; - - var compilation = context.Compilation; + var classDeclaration = (ClassDeclarationSyntax)context.Node; - // Retrieve the interface symbol - var gameStateInterface = compilation.GetTypeByMetadataName(gamestate); + // Get the semantic model + var semanticModel = context.SemanticModel; + var classSymbol = semanticModel.GetDeclaredSymbol(classDeclaration); - if (gameStateInterface == null) - { - // Interface not found, do nothing - return; - } + if (classSymbol == null) return null; + + // Check if it's an Aurora class and derives from GameState + if (!IsAuroraClass(classSymbol)) return null; + + var gameStateInterface = semanticModel.Compilation.GetTypeByMetadataName(GameStateInterface); + if (gameStateInterface == null) return null; + + if (!IsSubtypeOf(classSymbol, gameStateInterface)) return null; - // Find all classes in the compilation - var classes = compilation.SyntaxTrees - .SelectMany(syntaxTree => syntaxTree.GetRoot().DescendantNodes()) - .OfType() - .Select(declaration => ModelExtensions.GetDeclaredSymbol(compilation.GetSemanticModel(declaration.SyntaxTree), declaration)) - .OfType() - .Where(IsAuroraClass) - .Where(IsSubtypeOf(gameStateInterface)) - .Where(IsPartialClass) - .Where(classSymbol => !ignore.Contains(classSymbol.ToDisplayString())); - - foreach (var classSymbol in classes) + return classDeclaration; + } + + private static void Execute(Compilation compilation, ImmutableArray classes, SourceProductionContext context) + { + if (classes.IsDefaultOrEmpty) return; + + HashSet ignore = [GameStateInterface]; + + foreach (var classDeclaration in classes) { + var semanticModel = compilation.GetSemanticModel(classDeclaration.SyntaxTree); + var classSymbol = semanticModel.GetDeclaredSymbol(classDeclaration); + + if (classSymbol == null) continue; + if (ignore.Contains(classSymbol.ToDisplayString())) continue; + GenerateClassProperties(context, classSymbol); } } - private static Func IsSubtypeOf(INamedTypeSymbol gameStateInterface) + private static bool IsSubtypeOf(INamedTypeSymbol classSymbol, INamedTypeSymbol gameStateInterface) { - return classSymbol => + var currentType = classSymbol.BaseType; + while (currentType != null) { - var currentType = classSymbol.BaseType; - while (currentType != null) + if (SymbolEqualityComparer.Default.Equals(currentType, gameStateInterface)) { - if (SymbolEqualityComparer.Default.Equals(currentType, gameStateInterface)) - { - return true; - } - - currentType = currentType.BaseType; + return true; } - return false; - }; + currentType = currentType.BaseType; + } + + return false; } - private static void GenerateClassProperties(GeneratorExecutionContext context, INamedTypeSymbol classSymbol) + private static void GenerateClassProperties(SourceProductionContext context, INamedTypeSymbol classSymbol) { // Get all properties of the class var properties = GetClassProperties("", classSymbol, $"(({classSymbol.Name})t)."); @@ -106,14 +125,10 @@ public partial class {{classSymbol.Name}} } """; - // Add the generated source to the compilation context.AddSource(classSymbol.Name + ".g.cs", SourceText.From(source, Encoding.UTF8)); } - private static Func Selector() - { - return AccessorMethodSource; - } + private static Func Selector() => AccessorMethodSource; private static string AccessorMethodSource(PropertyAccess valueTuple) { @@ -123,7 +138,7 @@ private static string AccessorMethodSource(PropertyAccess valueTuple) private static IEnumerable GetClassProperties(string currentPath, INamedTypeSymbol type, string upperAccessPath) { - var namedTypeSymbols = GetBaseTypes(type); + var namedTypeSymbols = ClassUtils.GetBaseTypes(type); foreach (var currentType in namedTypeSymbols) { foreach (var member in currentType.GetMembers()) @@ -139,21 +154,19 @@ private static IEnumerable GetClassProperties(string currentPath var propertyType = property.Type; var accessPath = property.IsStatic ? GetStaticAccessPath(currentType, property) : upperAccessPath + property.Name; - - // Check if the type is a primitive type + if (propertyType.IsValueType || propertyType.SpecialType == SpecialType.System_String) { yield return new PropertyAccess(currentPath + property.Name, accessPath); continue; } - // Check if the type is a class if (propertyType.TypeKind is not (TypeKind.Class or TypeKind.Interface)) continue; var namedTypeSymbol = (INamedTypeSymbol)property.Type; if (!IsAuroraClass(namedTypeSymbol)) continue; - // prevent infinite recursions, this should look for all previous types actually + if (SymbolEqualityComparer.Default.Equals(namedTypeSymbol, currentType)) { continue; @@ -163,56 +176,21 @@ private static IEnumerable GetClassProperties(string currentPath var s = property.NullableAnnotation == NullableAnnotation.Annotated ? "?." : "."; var lowerAccessPath = property.IsStatic ? GetStaticAccessPath(currentType, property) : upperAccessPath + property.Name + s; - + foreach (var classProperty in GetClassProperties(upperProperty, namedTypeSymbol, lowerAccessPath)) { yield return classProperty; } } } - - } - - private static IEnumerable GetBaseTypes(INamedTypeSymbol type) - { - var currentType = type; - while (currentType != null) - { - yield return currentType; - currentType = currentType.BaseType; - } } private static bool IgnoredAttribute(AttributeData arg) - { - return arg.AttributeClass?.Name == "GameStateIgnoreAttribute"; - } + => arg.AttributeClass?.Name == "GameStateIgnoreAttribute"; private static string GetStaticAccessPath(INamedTypeSymbol currentType, IPropertySymbol property) - { - return currentType.ContainingNamespace + "." + currentType.Name + "." + property.Name + "."; - } + => currentType.ContainingNamespace + "." + currentType.Name + "." + property.Name + "."; private static bool IsAuroraClass(INamedTypeSymbol namedTypeSymbol) - { - return namedTypeSymbol.ToString().StartsWith("AuroraRgb."); - } - - private static bool IsPartialClass(INamedTypeSymbol namedTypeSymbol) - { - // Loop through all the declaration syntax references - foreach (var syntaxNode in namedTypeSymbol.DeclaringSyntaxReferences.Select(syntaxReference => syntaxReference.GetSyntax())) - { - // Check if the syntax node is a class declaration - if (syntaxNode is not ClassDeclarationSyntax classDeclaration) continue; - // Check if the class declaration has the 'partial' modifier - if (classDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword)) - { - return true; - } - } - - // If no 'partial' modifier is found, return false - return false; - } + => namedTypeSymbol.ToString().StartsWith("AuroraRgb."); } \ No newline at end of file diff --git a/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/OverrideSetterSourceGenerator.cs b/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/OverrideSetterSourceGenerator.cs index 712b6ef28..421789bcb 100644 --- a/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/OverrideSetterSourceGenerator.cs +++ b/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/OverrideSetterSourceGenerator.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Text; using Microsoft.CodeAnalysis; @@ -16,81 +17,107 @@ internal class PropertySetter(string accessPath, ITypeSymbol valueType, INamedTy public bool IsOverride { get; } = propertyIsOverride; } -[Generator] -public class OverrideSetterSourceGenerator : ISourceGenerator +[Generator(LanguageNames.CSharp)] +public class OverrideSetterSourceGenerator : IIncrementalGenerator { - public void Initialize(GeneratorInitializationContext context) - { - } - - public void Execute(GeneratorExecutionContext context) + public void Initialize(IncrementalGeneratorInitializationContext context) { const string layerHandlerProperties = "AuroraRgb.Settings.Layers.LayerHandlerProperties"; - var compilation = context.Compilation; - - // Retrieve the interface symbol - var handlerPropertiesInterface = compilation.GetTypeByMetadataName(layerHandlerProperties); - - if (handlerPropertiesInterface == null) - { - // Interface not found, do nothing - return; - } - - // Find all classes in the compilation - var classes = compilation.SyntaxTrees - .SelectMany(syntaxTree => syntaxTree.GetRoot().DescendantNodes()) - .OfType() - .Select(declaration => compilation.GetSemanticModel(declaration.SyntaxTree).GetDeclaredSymbol(declaration)) - .OfType() - .Where(IsSubtypeOf(handlerPropertiesInterface)); - - List generatedClasses = [handlerPropertiesInterface]; - foreach (var classSymbol in classes) - { - try + // Get all class declarations and their semantic models + var classDeclarations = + context.SyntaxProvider + .CreateSyntaxProvider( + predicate: static (s, _) => s is ClassDeclarationSyntax, + transform: (ctx, _) => + { + var syntax = (ClassDeclarationSyntax)ctx.Node; + var symbol = ctx.SemanticModel.GetDeclaredSymbol(syntax) as INamedTypeSymbol; + return (Syntax: syntax, Symbol: symbol); + }); + + // Get the handlerPropertiesInterface + var handlerInterface = context.CompilationProvider.Select((c, _) => c.GetTypeByMetadataName(layerHandlerProperties)); + + // Combine the class declarations with the interface + var relevantClasses = classDeclarations + .Where(tuple => tuple.Symbol != null) + .Combine(handlerInterface.Select((i, _) => i!)) + .Where(tuple => IsSubtypeOf(tuple.Right)(tuple.Left.Symbol!)) + .Select((tuple, _) => (Class: tuple.Left.Symbol!, Interface: tuple.Right)); + + // Generate the individual class logic files + context.RegisterSourceOutput(relevantClasses, + (spc, tuple) => { - GenerateLogic(context, classSymbol); - generatedClasses.Add(classSymbol); - GenerateLogicOverridePartial(context, classSymbol); - } - catch (Exception e) + try + { + GenerateLogic(spc, tuple.Class); + GenerateLogicOverridePartial(spc, tuple.Class); + } + catch (Exception e) + { + spc.ReportDiagnostic(Diagnostic.Create( + new DiagnosticDescriptor( + "ASG001", + "Generator Error", + "Error generating source: {0}", + "SourceGenerator", + DiagnosticSeverity.Error, + true), + Location.None, + e.ToString())); + } + }); + + // Collect all generated classes for the GeneratedLogics file + var allGeneratedClasses = + relevantClasses + .Collect() + .Select((items, _) => + { + var result = new List(); + if (items.Any()) + { + result.Add(items.First().Interface); + result.AddRange(items.Select(i => i.Class)); + } + + return result.ToImmutableArray(); + }); + + // Generate the GeneratedLogics file + context.RegisterSourceOutput(allGeneratedClasses, + (spc, classes) => { - Console.Error.WriteLine("uh oh"); - Console.Error.WriteLine(e); - } - } - var source = $$""" - // Auto-generated code - // {{DateTime.Now}} - #nullable enable + var source = $$""" + // Auto-generated code + // {{DateTime.Now}} + #nullable enable - using System; - using System.Collections.Generic; - - namespace AuroraRgb.Settings.Layers - { - public static class GeneratedLogics - { - private static readonly Dictionary> _innerLogics = new() - { - {{string.Join(",\n", generatedClasses.Select(s => s).Where(c => !c.IsGenericType).Select(GetLogicInstanceSource))}} - }; - public static IReadOnlyDictionary> LogicMap => _innerLogics; - } - } - """; + using System; + using System.Collections.Generic; - // Add the generated source to the compilation - context.AddSource("GeneratedLogics.g.cs", SourceText.From(source, Encoding.UTF8)); - } + namespace AuroraRgb.Settings.Layers + { + public static class GeneratedLogics + { + private static readonly Dictionary> _innerLogics = new() + { + {{string.Join(",\n", classes.Where(c => !c.IsGenericType).Select(GetLogicInstanceSource))}} + }; + public static IReadOnlyDictionary> LogicMap => _innerLogics; + } + } + """; - private static string GetLogicInstanceSource(INamedTypeSymbol arg) - { - return $"[\"{arg.Name}\"]\t=\t() => new {arg}Logic()"; + spc.AddSource("GeneratedLogics.g.cs", SourceText.From(source, Encoding.UTF8)); + }); } + // Rest of the existing helper methods remain the same + private static string GetLogicInstanceSource(INamedTypeSymbol arg) => $"[\"{arg.Name}\"]\t=\t() => new {arg}Logic()"; + private static Func IsSubtypeOf(INamedTypeSymbol upperClass) { return classSymbol => @@ -110,7 +137,7 @@ private static Func IsSubtypeOf(INamedTypeSymbol upperCl }; } - private static void GenerateLogic(GeneratorExecutionContext context, INamedTypeSymbol propertiesClassSymbol) + private static void GenerateLogic(SourceProductionContext context, INamedTypeSymbol propertiesClassSymbol) { string[] ignore = ["LayerHandlerProperties"]; @@ -119,65 +146,61 @@ private static void GenerateLogic(GeneratorExecutionContext context, INamedTypeS return; } - // Get all properties of the class var properties = GetClassProperties(propertiesClassSymbol) .Where(p => p.AccessPath != "Logic") .ToList(); var logicClassName = AppendClassName(propertiesClassSymbol.Name, "Logic"); - Console.WriteLine(logicClassName); var genericParams = ""; if (propertiesClassSymbol.IsGenericType) { - genericParams = "<" + string.Join(",", propertiesClassSymbol.TypeArguments.Select(t => t.ToString())) + ">"; + genericParams = "<" + string.Join(",", propertiesClassSymbol.TypeArguments.Select(t => t.ToString())) + ">"; } var source = $$""" - // Auto-generated code - // {{DateTime.Now}} - // GenerateClassProperties - #nullable enable - - using System; - using System.Collections.Generic; - - namespace {{propertiesClassSymbol.ContainingNamespace}} - { - public partial class {{logicClassName}}{{genericParams}} : {{AppendClassName(propertiesClassSymbol.BaseType.ToString(), "Logic")}} - { - {{string.Join("\n", properties.Where(p => !p.IsOverride).Select((Func)PropertyDefinitionSource))}} - - private static readonly Dictionary> InnerSetters = new() - { - {{string.Join(",\n", properties.Select((Func)SetMethodSource))}} - }; - public override IReadOnlyDictionary> SetterMap => InnerSetters; - } - } - """; + // Auto-generated code + // {{DateTime.Now}} + // GenerateClassProperties + #nullable enable + + using System; + using System.Collections.Generic; + + namespace {{propertiesClassSymbol.ContainingNamespace}} + { + public partial class {{logicClassName}}{{genericParams}} : {{AppendClassName(propertiesClassSymbol.BaseType.ToString(), "Logic")}} + { + {{string.Join("\n", properties.Where(p => !p.IsOverride).Select((Func)PropertyDefinitionSource))}} + + private static readonly Dictionary> InnerSetters = new() + { + {{string.Join(",\n", properties.Select((Func)SetMethodSource))}} + }; + public override IReadOnlyDictionary> SetterMap => InnerSetters; + } + } + """; var genericFileSuffix = string.Join(".", propertiesClassSymbol.TypeArguments.Select(t => t.ToString())); - // Add the generated source to the compilation context.AddSource(logicClassName + genericFileSuffix + ".g.cs", SourceText.From(source, Encoding.UTF8)); } - private void GenerateLogicOverridePartial(GeneratorExecutionContext context, INamedTypeSymbol classSymbol) + private static void GenerateLogicOverridePartial(SourceProductionContext context, INamedTypeSymbol classSymbol) { - // Get all properties of the class var logicClassName = AppendClassName(classSymbol.ToString(), "Logic"); var genericParams = ""; if (classSymbol.IsGenericType) { - genericParams = "<" + string.Join(",", classSymbol.TypeArguments.Select(t => t.ToString())) + ">"; + genericParams = "<" + string.Join(",", classSymbol.TypeArguments.Select(t => t.ToString())) + ">"; } var genericConstraints = ""; if (classSymbol.IsGenericType) { var constraints = classSymbol.TypeParameters - .SelectMany(t => t.ConstraintTypes.Select(c => t.Name + " : " + c)); - genericConstraints = "where " + string.Join(",", constraints); + .SelectMany(t => t.ConstraintTypes.Select(c => t.Name + " : " + c)); + genericConstraints = "where " + string.Join(",", constraints); } var source = $$""" @@ -196,14 +219,13 @@ namespace {{classSymbol.ContainingNamespace}} public partial class {{classSymbol.Name}}{{genericParams}} {{genericConstraints}} { - [GameStateIgnore, JsonIgnore] - public {{logicClassName}}? Logic { get; set; } + [GameStateIgnore, JsonIgnore] + public {{logicClassName}}? Logic { get; set; } } } """; var genericFileSuffix = string.Join(".", classSymbol.TypeArguments.Select(t => t.ToString())); - // Add the generated source to the compilation context.AddSource(classSymbol.Name + genericFileSuffix + ".g.cs", SourceText.From(source, Encoding.UTF8)); } @@ -227,18 +249,19 @@ private static string PropertyDefinitionSource(PropertySetter valueTuple) var propertyType = valueType.ToString().EndsWith("?") ? valueType.ToString() : valueType + "?"; var firstCharIndex = accessPath.LastIndexOf('_') + 1; - + var lowerPropertyName = char.ToLower(accessPath[firstCharIndex]) + accessPath.Substring(firstCharIndex + 1); var upperPropertyName = char.ToUpper(accessPath[firstCharIndex]) + accessPath.Substring(firstCharIndex + 1); - string[] properties = [ + string[] properties = + [ lowerPropertyName, upperPropertyName, $"_{lowerPropertyName}", $"_{upperPropertyName}", accessPath ]; - + var propertySources = properties.Distinct() .Except([fieldName]) .Select(p => $$""" @@ -252,13 +275,13 @@ private static string PropertyDefinitionSource(PropertySetter valueTuple) } """); - + return $""" - // field - public {propertyType} {fieldName}; - - {string.Join("", propertySources)} - """; + // field + public {propertyType} {fieldName}; + + {string.Join("", propertySources)} + """; } private static string SetMethodSource(PropertySetter valueTuple) @@ -315,10 +338,11 @@ private static string RemoveNullable(string typeName) private static IEnumerable GetClassProperties(INamedTypeSymbol classSymbol) { - return GetBaseTypes(classSymbol) + return ClassUtils.GetBaseTypes(classSymbol) .SelectMany(c => c.GetMembers()) .OfType() - .Where(property => property.GetMethod?.DeclaredAccessibility.HasFlag(Accessibility.Public) ?? (property.SetMethod?.DeclaredAccessibility.HasFlag(Accessibility.Internal) ?? false)) + .Where(property => property.GetMethod?.DeclaredAccessibility.HasFlag(Accessibility.Public) ?? + (property.SetMethod?.DeclaredAccessibility.HasFlag(Accessibility.Internal) ?? false)) .Where(property => !property.IsStatic) .Where(p => IsLogicOverridable(p) || !p.Name.StartsWith("_")) .Select(symbol => GetMemberSetter(symbol, classSymbol)); @@ -329,19 +353,9 @@ private static bool IsLogicOverridable(IPropertySymbol propertySymbol) return propertySymbol.GetAttributes().Any(a => a.AttributeClass?.Name == "LogicOverridableAttribute"); } - private static IEnumerable GetBaseTypes(INamedTypeSymbol type) - { - var currentType = type; - while (currentType != null) - { - yield return currentType; - currentType = currentType.BaseType; - } - } - private static PropertySetter GetMemberSetter(IPropertySymbol property, INamedTypeSymbol baseType) { - return new PropertySetter(property.Name, property.Type, baseType, !SymbolEqualityComparer.IncludeNullability.Equals(property.ContainingType, baseType)); + return new PropertySetter(property.Name, property.Type, baseType, !SymbolEqualityComparer.IncludeNullability.Equals(property.ContainingType, baseType)); } private static bool IsAuroraClass(INamedTypeSymbol? namedTypeSymbol) diff --git a/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/Properties/launchSettings.json b/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/Properties/launchSettings.json index b26726185..0d4004984 100644 --- a/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/Properties/launchSettings.json +++ b/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Generators": { "commandName": "DebugRoslynComponent", - "targetProject": "../../Project-Aurora/Project-Aurora.csproj" + "targetProject": "..\\..\\Project-Aurora\\Project-Aurora.csproj" } }, "$schema": "https://json.schemastore.org/launchsettings.json" diff --git a/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/WrapperGenerator.cs b/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/WrapperGenerator.cs new file mode 100644 index 000000000..b194e66cb --- /dev/null +++ b/Project-Aurora/AuroraSourceGenerator/AuroraSourceGenerator/WrapperGenerator.cs @@ -0,0 +1,273 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; + +namespace AuroraSourceGenerator; + +[Generator(LanguageNames.CSharp)] +public class WrapperGenerator : IIncrementalGenerator +{ + private const string AttributeNamespace = "AuroraSourceGenerator"; + private const string DelegateToAttributeClassname = "DelegateToAttribute"; + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + // Register the attribute + context.RegisterPostInitializationOutput(ctx => ctx.AddSource( + "DelegateToAttribute.g.cs", + SourceText.From($$""" + using System; + + namespace {{AttributeNamespace}} + { + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class {{DelegateToAttributeClassname}}(string field) : System.Attribute + { + public string Field { get; } = field; + } + } + """, Encoding.UTF8) + )); + + // Create a pipeline for finding decorated classes + var classDeclarations = context.SyntaxProvider + .CreateSyntaxProvider( + predicate: static (s, _) => s is ClassDeclarationSyntax, + transform: static (ctx, _) => GetClassToGenerate(ctx)) + .Where(static m => m is not null); + + // Register the source output + context.RegisterSourceOutput(classDeclarations, + static (spc, classInfo) => Execute(spc, classInfo!)); + } + + private static ClassToGenerate? GetClassToGenerate(GeneratorSyntaxContext context) + { + var wrappedClass = (ClassDeclarationSyntax)context.Node; + var model = context.SemanticModel; + + if (model.GetDeclaredSymbol(wrappedClass) is not INamedTypeSymbol wrapperClass) + { + return null; + } + + var delegateAttribute = wrapperClass.GetAttributes() + .FirstOrDefault(attr => DelegateToAttributeClassname == attr.AttributeClass?.Name); + + if (delegateAttribute == null) + { + return null; + } + + var delegateFieldName = delegateAttribute.ConstructorArguments[0].Value?.ToString(); + if (delegateFieldName == null) + { + return null; + } + + var delegateField = wrapperClass.GetMembers() + .OfType() + .FirstOrDefault(field => field.Name == delegateFieldName); + + if (delegateField == null) + { + return null; + } + + var definedMethodNames = ClassUtils.GetBaseTypes(wrapperClass) + .SelectMany(c => c.GetMembers()) + .OfType() + .Where(m => !m.IsStatic && m.MethodKind == MethodKind.Ordinary) + .Where(IsPublic) + .Select(s => s.Name) + .ToImmutableHashSet(); + + var definedPropertyNames = ClassUtils.GetBaseTypes(wrapperClass) + .SelectMany(c => c.GetMembers()) + .OfType() + .Where(p => !p.IsStatic) + .Where(IsPublic) + .Select(s => s.Name) + .ToImmutableHashSet(); + + var delegateClass = delegateField.Type; + + // Collect methods and properties + var methods = ClassUtils.GetBaseTypes(delegateClass) + .Union(ClassUtils.GetAllInterfaces(wrapperClass), SymbolEqualityComparer.IncludeNullability) + .SelectMany(c => c.GetMembers()) + .OfType() + .Distinct(SymbolEqualityComparer.IncludeNullability) + .Where(m => !m.IsStatic && m.MethodKind == MethodKind.Ordinary) + .Where(IsPublic) + .Where(m => !definedMethodNames.Contains(m.Name)) + .Select(GetMethodToGenerate) + .ToList(); + + var properties = ClassUtils.GetBaseTypes(delegateClass) + .SelectMany(c => c.GetMembers()) + .OfType() + .Where(p => !p.IsStatic) + .Where(IsPublic) + .Where(m => !definedPropertyNames.Contains(m.Name)) + .Select(GetPropertyToGenerate) + .ToList(); + + return new ClassToGenerate( + wrapperClass.Name, + wrapperClass.ContainingNamespace.ToDisplayString(), + delegateFieldName, + methods, + properties); + } + + private static void Execute(SourceProductionContext context, ClassToGenerate classInfo) + { + var source = GenerateWrapperClass(classInfo); + context.AddSource($"{classInfo.ClassName}.Wrapper.g.cs", + SourceText.From(source, Encoding.UTF8)); + } + + private static string GenerateWrapperClass(ClassToGenerate classInfo) + { + var sb = new StringBuilder(); + + sb.AppendLine($$""" + namespace {{classInfo.Namespace}} + { + public partial class {{classInfo.ClassName}} + { + """ + ); + + // Generate delegating methods and properties + foreach (var property in classInfo.Properties) + { + sb.AppendLine($$""" + public {{property.Type}} {{property.Name}} + { + """ + ); + if (property.HasGetter) + { + sb.AppendLine($" get => {classInfo.FieldName}.{property.Name};"); + } + if (property.HasSetter) + { + sb.AppendLine($" set => {classInfo.FieldName}.{property.Name} = value;"); + } + sb.AppendLine(""" }""" + ); + } + + foreach (var method in classInfo.Methods) + { + if (method.ReturnType != "void") + { + sb.AppendLine($$""" + public {{method.ReturnType}} {{method.Name}}({{method.Parameters}}) + { + return {{classInfo.FieldName}}.{{method.Name}}({{method.ParameterNames}}); + } + """ + ); + } + else + { + sb.AppendLine($$""" + public {{method.ReturnType}} {{method.Name}}({{method.Parameters}}) + { + {{classInfo.FieldName}}.{{method.Name}}({{method.ParameterNames}}); + } + """ + ); + } + } + + sb.AppendLine(""" + } + } + """ + ); + + return sb.ToString(); + } + + private static bool IsPublic(ISymbol m) + { + return m.DeclaredAccessibility.HasFlag(Accessibility.Public); + } + + private static PropertyToGenerate GetPropertyToGenerate(IPropertySymbol p) + { + return new PropertyToGenerate(p.Name, p.Type.ToDisplayString(), p.GetMethod != null, p.SetMethod != null); + } + + private static MethodToGenerate GetMethodToGenerate(IMethodSymbol m) + { + return new MethodToGenerate( + m.Name, + m.ReturnType.ToDisplayString(), + string.Join(", ", m.Parameters.Select(ParameterNameWithModifiers)), + string.Join(", ", m.Parameters.Select(ReferenceName)) + ); + + static string ParameterNameWithModifiers(IParameterSymbol p) + { + var typeAndName = $"{p.Type.ToDisplayString()} {p.Name}"; + + var modifiers = p.RefKind switch + { + RefKind.None => string.Empty, + RefKind.In => "in ", + RefKind.Out => "out ", + RefKind.Ref => "ref ", + RefKind.RefReadOnlyParameter => "ref readonly ", + }; + var defaultPart = DefaultPart(p); + return modifiers + typeAndName + defaultPart; + } + + static string ReferenceName(IParameterSymbol p) + { + return p.RefKind switch + { + RefKind.RefReadOnlyParameter => "in " + p.Name, + _ => p.Name, + }; + } + + static string DefaultPart(IParameterSymbol p) + { + if (!p.HasExplicitDefaultValue) return string.Empty; + if (p.Type.TypeKind == TypeKind.Enum) + { + if (p.ExplicitDefaultValue == null) + return " = default"; + var enumName = p.Type.ToDisplayString(); + return $" = ({enumName})" + p.ExplicitDefaultValue; + } + if (p.Type.IsValueType) + { + return " = " + (p.ExplicitDefaultValue ?? "default").ToString().ToLowerInvariant(); + } + + return " = " + (p.ExplicitDefaultValue ?? "default"); + } + } + + private sealed record ClassToGenerate( + string ClassName, + string Namespace, + string FieldName, + List Methods, + List Properties); + + private sealed record MethodToGenerate(string Name, string ReturnType, string Parameters, string ParameterNames); + + private sealed record PropertyToGenerate(string Name, string Type, bool HasGetter, bool HasSetter); +} \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/Bitmaps/GdiPlus/GdiBitmap.cs b/Project-Aurora/Project-Aurora/Bitmaps/GdiPlus/GdiBitmap.cs index 120bd3bff..2dbc2c37c 100644 --- a/Project-Aurora/Project-Aurora/Bitmaps/GdiPlus/GdiBitmap.cs +++ b/Project-Aurora/Project-Aurora/Bitmaps/GdiPlus/GdiBitmap.cs @@ -15,14 +15,14 @@ public sealed class GdiBitmap : IAuroraBitmap //TODO expose SavePng method to interface and remove this public Bitmap Bitmap { get; } - private float _opacity = 1; - public float Opacity + private double _opacity = 1; + public double Opacity { get => _opacity; set { _opacity = value; - _colorMatrix.Matrix33 = value; + _colorMatrix.Matrix33 = (float)value; _imageAttributes.SetColorMatrix(_colorMatrix); } } @@ -56,10 +56,11 @@ private TextureBrush TextureBrush } _textureBrush = new TextureBrush(Bitmap, _dimension, _imageAttributes); - return _textureBrush; } } + + private static readonly SolidBrush ColorBrush = new(Color.Transparent); public GdiBitmap(int canvasWidth, int canvasHeight) { @@ -85,7 +86,7 @@ private void SetGraphics() public IBitmapReader CreateReader() { - return new GdiPartialCopyBitmapReader(Bitmap); + return new GdiPartialCopyBitmapReader(Bitmap, _opacity); } public void Reset() @@ -113,16 +114,11 @@ public void DrawRectangle(Pen pen, RectangleF dimension) public void DrawRectangle(EffectLayer brush) { - var gdiBitmap = GetGdiBitmap(brush.GetBitmap()); + var bitmapEffectLayer = (BitmapEffectLayer)brush; + var gdiBitmap = GetGdiBitmap(bitmapEffectLayer.GetBitmap()); DrawOver(gdiBitmap); } - public void DrawRectangle(EffectLayer brush, Rectangle dimension) - { - var gdiBitmap = GetGdiBitmap(brush.GetBitmap()); - DrawRectangle(gdiBitmap.TextureBrush, dimension); - } - public void DrawRectangle(IAuroraBrush brush, RectangleF dimension) { DrawRectangle(brush.GetBrush(), dimension); diff --git a/Project-Aurora/Project-Aurora/Bitmaps/GdiPlus/GdiPartialCopyBitmapReader.cs b/Project-Aurora/Project-Aurora/Bitmaps/GdiPlus/GdiPartialCopyBitmapReader.cs index ee4ba0924..69c41bd6b 100644 --- a/Project-Aurora/Project-Aurora/Bitmaps/GdiPlus/GdiPartialCopyBitmapReader.cs +++ b/Project-Aurora/Project-Aurora/Bitmaps/GdiPlus/GdiPartialCopyBitmapReader.cs @@ -1,103 +1,196 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; using Common.Utils; namespace AuroraRgb.Bitmaps.GdiPlus; public sealed class GdiPartialCopyBitmapReader : IBitmapReader { - //B, G, R, A - private static readonly long[] ColorData = [0L, 0L, 0L, 0L]; - private static readonly Dictionary Bitmaps = new(20); + private const int SmallestBufferLength = 32; + private static readonly Dictionary Bitmaps = new(20); + // ReSharper disable once CollectionNeverQueried.Local //to keep reference - private static readonly Dictionary BitmapBuffers = new(20); + private static readonly Dictionary BitmapBuffers = new(20); private readonly Bitmap _bitmap; - private readonly Size _bitmapSize = Size.Empty; private readonly RectangleF _dimension; + private readonly double _opacity; + private readonly Vector256 _zeroVector = Vector256.Zero; + + private Color _transparentColor = Color.Transparent; + private Color _currentColor = Color.Black; + + private readonly int[] _emptySmallestBuffer = new int[SmallestBufferLength]; - public GdiPartialCopyBitmapReader(Bitmap bitmap) + public GdiPartialCopyBitmapReader(Bitmap bitmap, double opacity) { _bitmap = bitmap; + _opacity = opacity; var graphicsUnit = GraphicsUnit.Pixel; _dimension = bitmap.GetBounds(ref graphicsUnit); - - _bitmapSize = bitmap.Size; } /** - * Gets average color of region, ignoring transparency - * NOT thread-safe + * Optimized with SIMD instructions where available */ - public Color GetRegionColor(Rectangle rectangle) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref readonly Color GetRegionColor(Rectangle rectangle) { if (rectangle.Width == 0 || rectangle.Height == 0 || !_dimension.Contains(rectangle)) - return Color.Black; - - ColorData[0] = 0L; - ColorData[1] = 0L; - ColorData[2] = 0L; - ColorData[3] = 0L; + return ref _transparentColor; - if (!Bitmaps.TryGetValue(rectangle.Size, out var buff)) + var area = rectangle.Width * rectangle.Height; + var bufferArea = Math.Max(area, SmallestBufferLength); + if (!Bitmaps.TryGetValue(bufferArea, out var buff)) { - var bitmapBuffer = new int[rectangle.Width * rectangle.Height]; - BitmapBuffers[rectangle.Size] = bitmapBuffer; + buff = CreateBuffer(rectangle); + Bitmaps[bufferArea] = buff; + } - var buffer = Marshal.AllocHGlobal(bitmapBuffer.Length * sizeof(int)); - Marshal.Copy(bitmapBuffer, 0, buffer, bitmapBuffer.Length); - // Create new bitmap data. - buff = new BitmapData - { - Width = rectangle.Width, - Height = rectangle.Height, - PixelFormat = PixelFormat.Format32bppArgb, - Stride = rectangle.Width * sizeof(int), - Scan0 = buffer - }; - - Bitmaps[rectangle.Size] = buff; + if (area < SmallestBufferLength) + { + // clear the padded array + Array.Copy(_emptySmallestBuffer, 0, BitmapBuffers[SmallestBufferLength], 0, _emptySmallestBuffer.Length); } var srcData = _bitmap.LockBits( rectangle, - (ImageLockMode)5, //ImageLockMode.UserInputBuffer | ImageLockMode.ReadOnly - PixelFormat.Format32bppRgb, buff); - var scan0 = srcData.Scan0; + (ImageLockMode)5, //ImageLockMode.UserInputBuffer | ImageLockMode.ReadOnly + PixelFormat.Format32bppArgb, + buff); + + var totals = ProcessPixels(srcData.Scan0, area); + + _bitmap.UnlockBits(srcData); + + var divider = area / _opacity; + _currentColor = CommonColorUtils.FastColor( + (byte)(totals.R / divider), + (byte)(totals.G / divider), + (byte)(totals.B / divider), + (byte)(totals.A / divider) + ); + return ref _currentColor; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe (long R, long G, long B, long A) ProcessPixels(in IntPtr scan0, int area) + { + var p = (byte*)scan0; + long sumB = 0, sumG = 0, sumR = 0, sumA = 0; - var rectangleHeight = rectangle.Height; - var rectangleWidth = rectangle.Width; - unsafe + if (Avx2.IsSupported && area >= 32) { - var p = (byte*)(void*)scan0; + var vectorSumB = _zeroVector; + var vectorSumG = _zeroVector; + var vectorSumR = _zeroVector; + var vectorSumA = _zeroVector; - var j = 0; - for (var y = 0; y < rectangleHeight; y++) + // Process 32 pixels at a time (32 * 4 bytes = 128 bytes) + var fullVector = Vector256.Create(0xFF); + var vectorCount = area / 32; + for (var i = 0; i < vectorCount; i++) { - for (var x = 0; x < rectangleWidth; x++) - { - ColorData[0] += p[j++]; - ColorData[1] += p[j++]; - ColorData[2] += p[j++]; - ColorData[3] += p[j++]; - } + var offset = i * 128; + var vector1 = Avx.LoadVector256((int*)(p + offset)); + var vector2 = Avx.LoadVector256((int*)(p + offset + 32)); + var vector3 = Avx.LoadVector256((int*)(p + offset + 64)); + var vector4 = Avx.LoadVector256((int*)(p + offset + 96)); + + vectorSumB = Avx2.Add(vectorSumB, Avx2.And(vector1, fullVector)); + vectorSumG = Avx2.Add(vectorSumG, Avx2.And(Avx2.ShiftRightLogical(vector1, 8), fullVector)); + vectorSumR = Avx2.Add(vectorSumR, Avx2.And(Avx2.ShiftRightLogical(vector1, 16), fullVector)); + vectorSumA = Avx2.Add(vectorSumA, Avx2.And(Avx2.ShiftRightLogical(vector1, 24), fullVector)); + + // Repeat for other vectors... + vectorSumB = Avx2.Add(vectorSumB, Avx2.And(vector2, fullVector)); + vectorSumG = Avx2.Add(vectorSumG, Avx2.And(Avx2.ShiftRightLogical(vector2, 8), fullVector)); + vectorSumR = Avx2.Add(vectorSumR, Avx2.And(Avx2.ShiftRightLogical(vector2, 16), fullVector)); + vectorSumA = Avx2.Add(vectorSumA, Avx2.And(Avx2.ShiftRightLogical(vector2, 24), fullVector)); + + vectorSumB = Avx2.Add(vectorSumB, Avx2.And(vector3, fullVector)); + vectorSumG = Avx2.Add(vectorSumG, Avx2.And(Avx2.ShiftRightLogical(vector3, 8), fullVector)); + vectorSumR = Avx2.Add(vectorSumR, Avx2.And(Avx2.ShiftRightLogical(vector3, 16), fullVector)); + vectorSumA = Avx2.Add(vectorSumA, Avx2.And(Avx2.ShiftRightLogical(vector3, 24), fullVector)); + + vectorSumB = Avx2.Add(vectorSumB, Avx2.And(vector4, fullVector)); + vectorSumG = Avx2.Add(vectorSumG, Avx2.And(Avx2.ShiftRightLogical(vector4, 8), fullVector)); + vectorSumR = Avx2.Add(vectorSumR, Avx2.And(Avx2.ShiftRightLogical(vector4, 16), fullVector)); + vectorSumA = Avx2.Add(vectorSumA, Avx2.And(Avx2.ShiftRightLogical(vector4, 24), fullVector)); } + + // Sum up the vector lanes + sumB = SumVector256(vectorSumB); + sumG = SumVector256(vectorSumG); + sumR = SumVector256(vectorSumR); + sumA = SumVector256(vectorSumA); + + // Process remaining pixels + var processed = vectorCount * 32; + var remaining = area - processed; + p += processed * 4; + area = remaining; } - _bitmap.UnlockBits(srcData); - var area = ColorData[3] / 255; - if (area == 0) + // Process remaining pixels or all pixels if AVX2 is not supported + var end = area * 4; + for (var j = 0; j < end;) { - return Color.Transparent; + sumB += p[j++]; + sumG += p[j++]; + sumR += p[j++]; + sumA += p[j++]; } - return CommonColorUtils.FastColor( - (byte) (ColorData[2] / area), (byte) (ColorData[1] / area), (byte) (ColorData[0] / area) - ); + + return (sumR, sumG, sumB, sumA); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static long SumVector256(in Vector256 vector) + { + var sum = 0L; + for (var i = 0; i < 8; i++) + { + sum += vector.GetElement(i); + } + + return sum; + } + + private static BitmapData CreateBuffer(in Rectangle rectangle) + { + var area = rectangle.Width * rectangle.Height; + var bufferArea = Math.Max(area, SmallestBufferLength); + var bitmapBuffer = new int[bufferArea]; + BitmapBuffers[bufferArea] = bitmapBuffer; + + var buffer = Marshal.AllocHGlobal(bitmapBuffer.Length * sizeof(int)); + Marshal.Copy(bitmapBuffer, 0, buffer, bitmapBuffer.Length); + + return new BitmapData + { + Width = rectangle.Width, + Height = rectangle.Height, + PixelFormat = PixelFormat.Format32bppArgb, + Stride = rectangle.Width * sizeof(int), + Scan0 = buffer + }; } public void Dispose() { + foreach (var bitmapData in Bitmaps.Values) + { + Marshal.FreeHGlobal(bitmapData.Scan0); + } + + Bitmaps.Clear(); + BitmapBuffers.Clear(); } } \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/Bitmaps/GdiPlus/GdiSingleCopyBitmapReader.cs b/Project-Aurora/Project-Aurora/Bitmaps/GdiPlus/GdiSingleCopyBitmapReader.cs index 185df4c2b..734cdbdd0 100644 --- a/Project-Aurora/Project-Aurora/Bitmaps/GdiPlus/GdiSingleCopyBitmapReader.cs +++ b/Project-Aurora/Project-Aurora/Bitmaps/GdiPlus/GdiSingleCopyBitmapReader.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Common.Utils; @@ -21,6 +22,9 @@ public sealed class GdiSingleCopyBitmapReader : IBitmapReader private readonly BitmapData _srcData; private readonly IntPtr _scan0; + + private Color _transparentColor = Color.Transparent; + private Color _currentColor = Color.Black; public GdiSingleCopyBitmapReader(Bitmap bitmap) { @@ -41,7 +45,7 @@ public GdiSingleCopyBitmapReader(Bitmap bitmap) _srcData = _bitmap.LockBits( rectangle, (ImageLockMode)5, //ImageLockMode.UserInputBuffer | ImageLockMode.ReadOnly - PixelFormat.Format32bppRgb, buff); + PixelFormat.Format32bppArgb, buff); _scan0 = _srcData.Scan0; } @@ -49,10 +53,11 @@ public GdiSingleCopyBitmapReader(Bitmap bitmap) * Gets average color of region, ignoring transparency * NOT thread-safe */ - public Color GetRegionColor(Rectangle rectangle) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref readonly Color GetRegionColor(Rectangle rectangle) { if (rectangle.Width == 0 || rectangle.Height == 0 || !_dimension.Contains(rectangle)) - return Color.Black; + return ref _transparentColor; ColorData[0] = 0L; ColorData[1] = 0L; @@ -91,11 +96,12 @@ public Color GetRegionColor(Rectangle rectangle) var area = ColorData[3] / 255; if (area == 0) { - return Color.Transparent; + return ref _transparentColor; } - return CommonColorUtils.FastColor( + _currentColor = CommonColorUtils.FastColor( (byte) (ColorData[2] / area), (byte) (ColorData[1] / area), (byte) (ColorData[0] / area) ); + return ref _currentColor; } private static BitmapData CreateBitmapData(Size size) diff --git a/Project-Aurora/Project-Aurora/Bitmaps/IAuroraBitmap.cs b/Project-Aurora/Project-Aurora/Bitmaps/IAuroraBitmap.cs index ea6a0498d..a2ada8d96 100644 --- a/Project-Aurora/Project-Aurora/Bitmaps/IAuroraBitmap.cs +++ b/Project-Aurora/Project-Aurora/Bitmaps/IAuroraBitmap.cs @@ -9,7 +9,7 @@ namespace AuroraRgb.Bitmaps; public interface IAuroraBitmap : IDisposable { - float Opacity { get; set; } + double Opacity { get; set; } IBitmapReader CreateReader(); @@ -18,7 +18,6 @@ public interface IAuroraBitmap : IDisposable void DrawRectangle(Brush brush, RectangleF dimension); void DrawRectangle(Pen pen, RectangleF dimension); void DrawRectangle(EffectLayer brush); - void DrawRectangle(EffectLayer brush, Rectangle dimension); void DrawRectangle(IAuroraBrush brush, Rectangle dimension); void DrawRectangle(IAuroraBrush brush, RectangleF dimension); void ReplaceRectangle(Brush brush, Rectangle dimension); diff --git a/Project-Aurora/Project-Aurora/Bitmaps/IBitmapReader.cs b/Project-Aurora/Project-Aurora/Bitmaps/IBitmapReader.cs index c93b350af..a5e43f965 100644 --- a/Project-Aurora/Project-Aurora/Bitmaps/IBitmapReader.cs +++ b/Project-Aurora/Project-Aurora/Bitmaps/IBitmapReader.cs @@ -1,9 +1,11 @@ using System; using System.Drawing; +using System.Runtime.CompilerServices; namespace AuroraRgb.Bitmaps; public interface IBitmapReader : IDisposable { - Color GetRegionColor(Rectangle rectangle); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + ref readonly Color GetRegionColor(Rectangle rectangle); } \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/Bitmaps/RuntimeChangingBitmap.cs b/Project-Aurora/Project-Aurora/Bitmaps/RuntimeChangingBitmap.cs index b12846495..22593de54 100644 --- a/Project-Aurora/Project-Aurora/Bitmaps/RuntimeChangingBitmap.cs +++ b/Project-Aurora/Project-Aurora/Bitmaps/RuntimeChangingBitmap.cs @@ -1,147 +1,19 @@ -using System.Drawing; -using System.Drawing.Drawing2D; -using AuroraRgb.Bitmaps.GdiPlus; +using AuroraRgb.Bitmaps.GdiPlus; using AuroraRgb.Bitmaps.Skia; -using AuroraRgb.BrushAdapters; -using AuroraRgb.EffectsEngine; -using AuroraRgb.Settings; +using AuroraSourceGenerator; namespace AuroraRgb.Bitmaps; -public sealed class RuntimeChangingBitmap : IAuroraBitmap +[DelegateTo(nameof(_bitmap))] +public sealed partial class RuntimeChangingBitmap : IAuroraBitmap { private readonly IAuroraBitmap _bitmap; - public float Opacity - { - get => _bitmap.Opacity; - set => _bitmap.Opacity = value; - } - public RuntimeChangingBitmap(int canvasWidth, int canvasHeight, bool readable) { _bitmap = new GdiBitmap(canvasWidth, canvasHeight); } - public IBitmapReader CreateReader() - { - return _bitmap.CreateReader(); - } - - public void Reset() - { - _bitmap.Reset(); - } - - public void DrawRectangle(Brush brush, Rectangle dimension) - { - _bitmap.DrawRectangle(brush, dimension); - } - - public void DrawRectangle(Brush brush, RectangleF dimension) - { - _bitmap.DrawRectangle(brush, dimension); - } - - public void DrawRectangle(Pen pen, RectangleF dimension) - { - _bitmap.DrawRectangle(pen, dimension); - } - - public void DrawRectangle(IAuroraBrush brush, RectangleF dimension) - { - _bitmap.DrawRectangle(brush, dimension); - } - - public void DrawRectangle(EffectLayer brush) - { - _bitmap.DrawRectangle(brush); - } - - public void DrawRectangle(EffectLayer brush, Rectangle dimension) - { - _bitmap.DrawRectangle(brush, dimension); - } - - public void DrawRectangle(IAuroraBrush brush, Rectangle dimension) - { - _bitmap.DrawRectangle(brush, dimension); - } - - public void ReplaceRectangle(Brush brush, Rectangle dimension) - { - _bitmap.ReplaceRectangle(brush, dimension); - } - - public void ReplaceRectangle(Brush brush, RectangleF dimension) - { - _bitmap.ReplaceRectangle(brush, dimension); - } - - public void ReplaceRectangle(IAuroraBrush brush, Rectangle dimension) - { - _bitmap.ReplaceRectangle(brush, dimension); - } - - public void ReplaceRectangle(IAuroraBrush brush, RectangleF dimension) - { - _bitmap.ReplaceRectangle(brush, dimension); - } - - public void PerformExclude(KeySequence excludeSequence) - { - _bitmap.PerformExclude(excludeSequence); - } - - public void OnlyInclude(KeySequence sequence) - { - _bitmap.OnlyInclude(sequence); - } - - public void SetClip(RectangleF boundsRaw) => _bitmap.SetClip(boundsRaw); - - public void SetTransform(Matrix value) => _bitmap.SetTransform(value); - - public void DrawEllipse(Pen pen, RectangleF dimension) - { - _bitmap.DrawEllipse(pen, dimension); - } - - public void FillEllipse(Brush pen, RectangleF dimension) - { - _bitmap.FillEllipse(pen, dimension); - } - - public void FillEllipse(IAuroraBrush brush, Rectangle dimension) - { - _bitmap.FillEllipse(brush, dimension); - } - - public void FillEllipse(IAuroraBrush brush, RectangleF dimension) - { - _bitmap.FillEllipse(brush, dimension); - } - - public void DrawImage(Image image, float x = 0, float y = 0, float width = 0, float height = 0) - { - _bitmap.DrawImage(image, x, y, width, height); - } - - public void DrawLine(Pen pen, PointF startPoint, PointF endPoint) - { - _bitmap.DrawLine(pen, startPoint, endPoint); - } - - public void Fill(Brush brush) - { - _bitmap.Fill(brush); - } - - public void Dispose() - { - _bitmap.Dispose(); - } - public GdiBitmap GetGdiBitmap() { if (_bitmap is GdiBitmap gdiBitmap) diff --git a/Project-Aurora/Project-Aurora/Bitmaps/Skia/AuroraCpuSkiaBitmap.cs b/Project-Aurora/Project-Aurora/Bitmaps/Skia/AuroraCpuSkiaBitmap.cs index ddaaafebb..4e1c3cbea 100644 --- a/Project-Aurora/Project-Aurora/Bitmaps/Skia/AuroraCpuSkiaBitmap.cs +++ b/Project-Aurora/Project-Aurora/Bitmaps/Skia/AuroraCpuSkiaBitmap.cs @@ -23,7 +23,8 @@ public override IBitmapReader CreateReader() public override void DrawRectangle(EffectLayer brush) { - var auroraSkiaBitmap = (AuroraCpuSkiaBitmap)GetSkiaBitmap(brush.GetBitmap()); + var bitmapEffectLayer = (BitmapEffectLayer)brush; + var auroraSkiaBitmap = (AuroraCpuSkiaBitmap)GetSkiaBitmap(bitmapEffectLayer.GetBitmap()); SkPaint.Color = new SKColor(255, 255, 255, (byte)(auroraSkiaBitmap.Opacity * 255)); var skiaBitmap = auroraSkiaBitmap._bitmap; Canvas.DrawBitmap(skiaBitmap, 0, 0, SkPaint); @@ -31,7 +32,8 @@ public override void DrawRectangle(EffectLayer brush) public override void DrawRectangle(EffectLayer brush, Rectangle dimension) { - var auroraSkiaBitmap = (AuroraCpuSkiaBitmap)GetSkiaBitmap(brush.GetBitmap()); + var bitmapEffectLayer = (BitmapEffectLayer)brush; + var auroraSkiaBitmap = (AuroraCpuSkiaBitmap)GetSkiaBitmap(bitmapEffectLayer.GetBitmap()); SkPaint.Color = new SKColor(255, 255, 255, (byte)(auroraSkiaBitmap.Opacity * 255)); var skiaBitmap = auroraSkiaBitmap._bitmap; var rectangle = SkiaRectangle(dimension); diff --git a/Project-Aurora/Project-Aurora/Bitmaps/Skia/AuroraVulkanSkiaBitmap.cs b/Project-Aurora/Project-Aurora/Bitmaps/Skia/AuroraVulkanSkiaBitmap.cs index 5964ee793..11aa6b7a1 100644 --- a/Project-Aurora/Project-Aurora/Bitmaps/Skia/AuroraVulkanSkiaBitmap.cs +++ b/Project-Aurora/Project-Aurora/Bitmaps/Skia/AuroraVulkanSkiaBitmap.cs @@ -46,7 +46,8 @@ public override IBitmapReader CreateReader() public override void DrawRectangle(EffectLayer brush) { - var auroraSkiaBitmap = (AuroraVulkanSkiaBitmap)GetSkiaBitmap(brush.GetBitmap()); + var bitmapEffectLayer = (BitmapEffectLayer)brush; + var auroraSkiaBitmap = (AuroraVulkanSkiaBitmap)GetSkiaBitmap(bitmapEffectLayer.GetBitmap()); SkPaint.Color = new SKColor(255, 255, 255, (byte)(auroraSkiaBitmap.Opacity * 255)); var skiaBitmap = auroraSkiaBitmap._surface; Canvas.DrawSurface(skiaBitmap, 0, 0, SkPaint); @@ -54,7 +55,8 @@ public override void DrawRectangle(EffectLayer brush) public override void DrawRectangle(EffectLayer brush, Rectangle dimension) { - var auroraSkiaBitmap = (AuroraVulkanSkiaBitmap)GetSkiaBitmap(brush.GetBitmap()); + var bitmapEffectLayer = (BitmapEffectLayer)brush; + var auroraSkiaBitmap = (AuroraVulkanSkiaBitmap)GetSkiaBitmap(bitmapEffectLayer.GetBitmap()); SkPaint.Color = new SKColor(255, 255, 255, (byte)(auroraSkiaBitmap.Opacity * 255)); var skiaBitmap = auroraSkiaBitmap._surface; var rectangle = SkiaRectangle(dimension); diff --git a/Project-Aurora/Project-Aurora/Bitmaps/Skia/SkiaBitmap.cs b/Project-Aurora/Project-Aurora/Bitmaps/Skia/SkiaBitmap.cs index a278a1814..4d8f08e59 100644 --- a/Project-Aurora/Project-Aurora/Bitmaps/Skia/SkiaBitmap.cs +++ b/Project-Aurora/Project-Aurora/Bitmaps/Skia/SkiaBitmap.cs @@ -31,7 +31,7 @@ protected virtual void Invalidate() { } - public float Opacity { get; set; } = 1.0f; + public double Opacity { get; set; } = 1.0; public abstract IBitmapReader CreateReader(); diff --git a/Project-Aurora/Project-Aurora/Bitmaps/Skia/SkiaBitmapReader.cs b/Project-Aurora/Project-Aurora/Bitmaps/Skia/SkiaBitmapReader.cs index 4a1f0b66b..3253c9e5a 100644 --- a/Project-Aurora/Project-Aurora/Bitmaps/Skia/SkiaBitmapReader.cs +++ b/Project-Aurora/Project-Aurora/Bitmaps/Skia/SkiaBitmapReader.cs @@ -1,4 +1,5 @@ using System.Drawing; +using System.Runtime.CompilerServices; using SkiaSharp; namespace AuroraRgb.Bitmaps.Skia; @@ -7,18 +8,23 @@ public sealed class SkiaBitmapReader(SKBitmap bitmap) : IBitmapReader { private readonly SKColor[] _pixels = bitmap.Pixels; - public Color GetRegionColor(Rectangle rectangle) + private Color _transparentColor = Color.Transparent; + private Color _currentColor = Color.Black; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref readonly Color GetRegionColor(Rectangle rectangle) { var skiaRectangle = SkiaUtils.SkiaRectangle(rectangle); - return GetAverageColorInRectangle(skiaRectangle); + return ref GetAverageColorInRectangle(skiaRectangle); } - private Color GetAverageColorInRectangle(SKRectI rect) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ref Color GetAverageColorInRectangle(SKRectI rect) { // Ensure the rectangle is within the bounds of the bitmap var bitmapWidth = bitmap.Width; rect = SKRectI.Intersect(rect, new SKRectI(0, 0, bitmapWidth, bitmap.Height)); - if (rect.IsEmpty) return Color.Transparent; + if (rect.IsEmpty) return ref _transparentColor; // Now calculate the average color from the subset long red = 0, green = 0, blue = 0, alpha = 0; @@ -44,7 +50,8 @@ private Color GetAverageColorInRectangle(SKRectI rect) var avgAlpha = (byte)(alpha / area); // Return the average color - return Color.FromArgb(avgAlpha, avgRed, avgGreen, avgBlue); + _currentColor = Color.FromArgb(avgAlpha, avgRed, avgGreen, avgBlue); + return ref _currentColor; } public void Dispose() diff --git a/Project-Aurora/Project-Aurora/Controls/Control_AnimationMixPresenter.xaml.cs b/Project-Aurora/Project-Aurora/Controls/Control_AnimationMixPresenter.xaml.cs index 7adcfae67..9c4dc2177 100644 --- a/Project-Aurora/Project-Aurora/Controls/Control_AnimationMixPresenter.xaml.cs +++ b/Project-Aurora/Project-Aurora/Controls/Control_AnimationMixPresenter.xaml.cs @@ -261,7 +261,6 @@ private async Task StartUpdate() private async void UserControl_Unloaded(object? sender, RoutedEventArgs e) { await StopUpdate(); - Global.effengine.ForceImageRender(null); } private async Task StopUpdate() diff --git a/Project-Aurora/Project-Aurora/EffectsEngine/BitmapRectangle.cs b/Project-Aurora/Project-Aurora/EffectsEngine/BitmapRectangle.cs index 782e896c5..19e259224 100644 --- a/Project-Aurora/Project-Aurora/EffectsEngine/BitmapRectangle.cs +++ b/Project-Aurora/Project-Aurora/EffectsEngine/BitmapRectangle.cs @@ -9,7 +9,7 @@ public sealed class BitmapRectangle : IEqualityComparer, IEquat public static readonly BitmapRectangle EmptyRectangle = new(); private readonly Rectangle _rectangle; - public Rectangle Rectangle => _rectangle; + public ref readonly Rectangle Rectangle => ref _rectangle; public bool IsEmpty => _rectangle.IsEmpty; diff --git a/Project-Aurora/Project-Aurora/EffectsEngine/EffectCanvas.cs b/Project-Aurora/Project-Aurora/EffectsEngine/EffectCanvas.cs index 0bca1feca..1b3732174 100644 --- a/Project-Aurora/Project-Aurora/EffectsEngine/EffectCanvas.cs +++ b/Project-Aurora/Project-Aurora/EffectsEngine/EffectCanvas.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Frozen; using System.Collections.Generic; +using System.Linq; using AuroraRgb.Settings; using Common.Devices; @@ -23,19 +24,6 @@ public sealed class EffectCanvas : IEqualityComparer, IEquatable bitmapMap) - { - Width = width; - Height = height; - BiggestSize = Math.Max(width, height); - BitmapMap = bitmapMap.ToFrozenDictionary(); - CanvasGridProperties = new(0, 0, width, height); - - EntireSequence = new(WholeFreeForm); - } - public int Width { get; } public int Height { get; } public int BiggestSize { get; } @@ -45,10 +33,15 @@ public EffectCanvas(int width, public float GridBaselineY => CanvasGridProperties.GridBaselineY; public FrozenDictionary BitmapMap { get; } + public DeviceKeys[] Keys { get; } public float WidthCenter { get; init; } public float HeightCenter { get; init; } + private readonly BitmapRectangle[] _keyRectangles = Enumerable.Range(0, Effects.MaxDeviceId + 1) + .Select(_ => BitmapRectangle.EmptyRectangle) + .ToArray(); + public CanvasGridProperties CanvasGridProperties { get => _canvasGridProperties; @@ -68,9 +61,27 @@ public CanvasGridProperties CanvasGridProperties public FreeFormObject WholeFreeForm => new(-CanvasGridProperties.GridBaselineX, -CanvasGridProperties.GridBaselineY, CanvasGridProperties.GridWidth, CanvasGridProperties.GridHeight); public KeySequence EntireSequence { get; private set; } - public BitmapRectangle GetRectangle(DeviceKeys key) + public EffectCanvas(int width, + int height, + Dictionary bitmapMap) + { + Width = width; + Height = height; + BiggestSize = Math.Max(width, height); + BitmapMap = bitmapMap.ToFrozenDictionary(); + foreach (var (key, value) in bitmapMap) + { + _keyRectangles[(int)key] = value; + } + Keys = bitmapMap.Keys.ToArray(); + CanvasGridProperties = new(0, 0, width, height); + + EntireSequence = new(WholeFreeForm); + } + + public ref readonly BitmapRectangle GetRectangle(DeviceKeys key) { - return BitmapMap.TryGetValue(key, out var rect) ? rect : BitmapRectangle.EmptyRectangle; + return ref _keyRectangles[(int)key]; } public bool Equals(EffectCanvas? other) diff --git a/Project-Aurora/Project-Aurora/EffectsEngine/EffectLayer.cs b/Project-Aurora/Project-Aurora/EffectsEngine/EffectLayer.cs index e929b7900..6a494ebb6 100755 --- a/Project-Aurora/Project-Aurora/EffectsEngine/EffectLayer.cs +++ b/Project-Aurora/Project-Aurora/EffectsEngine/EffectLayer.cs @@ -16,18 +16,12 @@ namespace AuroraRgb.EffectsEngine; -public enum LayerReadability -{ - None = 0, - Readable = 1, -} - /// /// A class representing a bitmap layer for effects /// -public class EffectLayer : IDisposable +public sealed class BitmapEffectLayer : EffectLayer { - public static EffectLayer EmptyLayer { get; } = new("EmptyLayer", true); + private static readonly Color TransparentColor = Color.Transparent; // Yes, this is no thread-safe but Aurora isn't supposed to take up resources // This is done to prevent memory leaks from creating new brushes @@ -37,44 +31,56 @@ public class EffectLayer : IDisposable }; private readonly string _name; + //TODO set readability based on GPU or not private readonly bool _readable = true; - protected IAuroraBitmap _colormap; + private IAuroraBitmap _colormap; private bool _invalidated = true; internal Rectangle Dimension; - [Obsolete("This creates too much garbage memory")] - public EffectLayer(string name) : this(name, Color.Transparent) - { - } + private IBitmapReader? _bitmapReader; + + public DeviceKeys[] ActiveKeys => Effects.Canvas.Keys; [Obsolete("This creates too much garbage memory")] - public EffectLayer(string name, Color color) + public BitmapEffectLayer(string name) { _name = name; _colormap = new RuntimeChangingBitmap(Effects.Canvas.Width, Effects.Canvas.Height, true); Dimension = new Rectangle(0, 0, Effects.Canvas.Width, Effects.Canvas.Height); + } - FillOver(color); + public BitmapEffectLayer(string name, bool persistent) : this(name) + { + if (!persistent) + return; + WeakEventManager.AddHandler(null, nameof(Effects.CanvasChanged), InvalidateColorMap); } - protected EffectLayer(string name, Color color, LayerReadability readable) + /// + /// Retrieves a color of the specified DeviceKeys key from the bitmap + /// + public Color Get(DeviceKeys key) { - _readable = readable == LayerReadability.Readable; - _name = name; - _colormap = new RuntimeChangingBitmap(Effects.Canvas.Width, Effects.Canvas.Height, true); - Dimension = new Rectangle(0, 0, Effects.Canvas.Width, Effects.Canvas.Height); + var keyRectangle = Effects.Canvas.GetRectangle(key); - FillOver(color); - WeakEventManager.AddHandler(null, nameof(Effects.CanvasChanged), InvalidateColorMap); + if (keyRectangle.IsEmpty) + return TransparentColor; + ref readonly var color = ref GetColor(in keyRectangle.Rectangle); + return color; } - public EffectLayer(string name, bool persistent) : this(name) + private ref readonly Color GetColor(ref readonly Rectangle rectangle) { - if (!persistent) - return; - WeakEventManager.AddHandler(null, nameof(Effects.CanvasChanged), InvalidateColorMap); + _bitmapReader ??= _colormap.CreateReader(); + return ref _bitmapReader.GetRegionColor(rectangle); + } + + public void Close() + { + _bitmapReader?.Dispose(); + _bitmapReader = null; } /// @@ -206,7 +212,7 @@ private void DrawRainbowGradient(float angle, float shift) rainbowBrush.Dispose(); } - public virtual void Dispose() + public void Dispose() { _excludeSequence = new KeySequence(); _keySequence = new KeySequence(); @@ -254,30 +260,27 @@ private LinearGradientBrush CreateRainbowBrush() return brush; } - /// - /// Fills the entire bitmap of the EffectLayer with a specified brush. - /// - /// Brush to be used during bitmap fill - public void Fill(Brush brush) + public void Fill(ref readonly Color color) { _colormap.Reset(); - _colormap.ReplaceRectangle(brush, Dimension); + _solidBrush.Color = (SimpleColor)color; + _colormap.ReplaceRectangle(_solidBrush, Dimension); Invalidate(); } - public void Fill(IAuroraBrush brush) + /// + /// Fills the entire bitmap of the EffectLayer with a specified brush. + /// + /// Brush to be used during bitmap fill + public void Fill(Brush brush) { _colormap.Reset(); _colormap.ReplaceRectangle(brush, Dimension); Invalidate(); } - /// - /// Paints over the entire bitmap of the EffectLayer with a specified color. - /// - /// Color to be used during bitmap fill - /// Itself - public void FillOver(Color color) + /// + public void FillOver(ref readonly Color color) { _colormap.Reset(); _solidBrush.Color = (SimpleColor)color; @@ -298,13 +301,6 @@ public void FillOver(Brush brush) Invalidate(); } - public void FillOver(IAuroraBrush brush) - { - _colormap.Reset(); - _colormap.DrawRectangle(brush, Dimension); - Invalidate(); - } - public void Clear() { _colormap.Fill(ClearingBrush); @@ -314,35 +310,22 @@ public void Clear() private FreeFormObject _lastFreeform = new(); private bool _ksChanged = true; - /// - /// Sets a specific DeviceKeys on the bitmap with a specified color. - /// - /// DeviceKey to be set - /// Color to be used - /// Itself - public void Set(DeviceKeys key, Color color) + + /// + public void Set(DeviceKeys key, ref readonly Color color) { - SetOneKey(key, color); + SetOneKey(key, in color); } - /// - /// Sets a specific DeviceKeys on the bitmap with a specified color. - /// - /// Array of DeviceKeys to be set - /// Color to be used - public void Set(DeviceKeys[] keys, Color color) + /// + public void Set(DeviceKeys[] keys, ref readonly Color color) { foreach (var key in keys) - SetOneKey(key, color); + SetOneKey(key, in color); } - /// - /// Sets a specific KeySequence on the bitmap with a specified color. - /// - /// KeySequence to specify what regions of the bitmap need to be changed - /// Color to be used - /// Itself - public void Set(KeySequence sequence, Color color) + /// + public void Set(KeySequence sequence, ref readonly Color color) { _solidBrush.Color = (SimpleColor)color; Set(sequence, _solidBrush); @@ -591,7 +574,7 @@ public void DrawTransformed(KeySequence sequence, Action render) /// /// DeviceKey to be set /// Color to be used - private void SetOneKey(DeviceKeys key, Color color) + private void SetOneKey(DeviceKeys key, ref readonly Color color) { _solidBrush.Color = (SimpleColor)color; SetOneKey(key, _solidBrush); @@ -607,7 +590,7 @@ private void SetOneKey(DeviceKeys key, Color color) /// /// DeviceKey to be set /// Brush to be used - protected virtual void SetOneKey(DeviceKeys key, Brush brush) + private void SetOneKey(DeviceKeys key, Brush brush) { var keyRectangle = Effects.Canvas.GetRectangle(key); _invalidated = true; @@ -657,52 +640,45 @@ public IAuroraBitmap GetBitmap() return _colormap; } - public void Add(EffectLayer other) - { - _ = this + other; - } - - /// - /// + Operator, sums two EffectLayer together. - /// - /// Left Hand Side EffectLayer - /// Right Hand Side EffectLayer - /// Left hand side EffectLayer, which is a combination of two passed EffectLayers - public static EffectLayer operator +(EffectLayer lhs, EffectLayer rhs) + /// + public EffectLayer Add(EffectLayer other) { - if (lhs == EmptyLayer) - { - return rhs; - } - - if (rhs == EmptyLayer) + if (ReferenceEquals(other, EmptyLayer.Instance)) { - return lhs; + return this; } - var g = lhs.GetGraphics(); + var g = GetGraphics(); + switch (other) { - g.DrawRectangle(rhs); + case BitmapEffectLayer bitmapEffectLayer: + g.DrawRectangle(bitmapEffectLayer); + break; + case NoRenderLayer noRenderLayer: + var activeKeys = noRenderLayer.ActiveKeys; + foreach (var key in activeKeys) + { + _solidBrush.Color = (SimpleColor)noRenderLayer.Get(key); + var bitmapRectangle = Effects.Canvas.GetRectangle(key).Rectangle; + g.DrawRectangle(_solidBrush, bitmapRectangle); + } + break; + case EmptyLayer: + break; + default: + throw new ArgumentOutOfRangeException(nameof(other), other, null); } - lhs.Invalidate(); - return lhs; + Invalidate(); + return this; } - /// - /// * Operator, Multiplies an EffectLayer by a double, adjusting opacity and color of the layer bitmap. - /// - /// EffectLayer to be adjusted - /// Double value that each bit in the bitmap will be multiplied by - /// The passed instance of EffectLayer with adjustments - public static EffectLayer operator *(EffectLayer layer, double value) + /// + public void SetOpacity(double value) { - if (!MathUtils.NearlyEqual(layer._colormap.Opacity,(float)value, 0.0001f)) - { - layer._colormap.Opacity = (float) value; - layer.Invalidate(); - } - return layer; + if (MathUtils.NearlyEqual(_colormap.Opacity, (float)value, 0.0001f)) return; + _colormap.Opacity = (float) value; + Invalidate(); } private KeySequenceType _previousSequenceType; @@ -714,7 +690,7 @@ public void Add(EffectLayer other) /// The foreground color, used as a "Progress bar color" /// The current progress value /// The maxiumum progress value - public void PercentEffect(Color foregroundColor, Color backgroundColor, KeySequence sequence, double value, + public void PercentEffect(Color foregroundColor, ref readonly Color backgroundColor, KeySequence sequence, double value, double total = 1.0D, PercentEffectType percentEffectType = PercentEffectType.Progressive, double flashPast = 0.0, bool flashReversed = false, bool blinkBackground = false) { @@ -757,7 +733,7 @@ public void PercentEffect(ColorSpectrum spectrum, KeySequence sequence, double v /// The foreground color, used as a "Progress bar color" /// The current progress value /// The maxiumum progress value - private void PercentEffect(Color foregroundColor, Color backgroundColor, IReadOnlyList keys, double value, + public void PercentEffect(Color foregroundColor, Color backgroundColor, IReadOnlyList keys, double value, double total, PercentEffectType percentEffectType = PercentEffectType.Progressive, double flashPast = 0.0, bool flashReversed = false, bool blinkBackground = false) { @@ -976,8 +952,9 @@ private void PercentEffect(Color foregroundColor, Color backgroundColor, FreeFor Invalidate(); } - public virtual void Invalidate() + public void Invalidate() { + _bitmapReader = null; _invalidated = true; } private void InvalidateColorMap(object? sender, EventArgs args) @@ -1065,10 +1042,7 @@ private void PercentEffect(ColorSpectrum spectrum, FreeFormObject freeform, doub private KeySequence _excludeSequence = new(); - /// - /// Excludes provided sequence from the layer (Applies a mask) - /// - /// The mask to be applied + /// public void Exclude(KeySequence sequence) { if (_excludeSequence.Equals(sequence)) @@ -1085,9 +1059,7 @@ public void Exclude(KeySequence sequence) Invalidate(); } - /// - /// Inlcudes provided sequence from the layer (Applies a mask) - /// + /// public void OnlyInclude(KeySequence sequence) { _colormap.OnlyInclude(sequence); diff --git a/Project-Aurora/Project-Aurora/EffectsEngine/Effects.cs b/Project-Aurora/Project-Aurora/EffectsEngine/Effects.cs index 37784acee..ca229b972 100755 --- a/Project-Aurora/Project-Aurora/EffectsEngine/Effects.cs +++ b/Project-Aurora/Project-Aurora/EffectsEngine/Effects.cs @@ -9,6 +9,7 @@ using AuroraRgb.Utils; using Common; using Common.Devices; +using Common.Utils; namespace AuroraRgb.EffectsEngine; @@ -104,9 +105,27 @@ public class Effects(Task deviceManager) DeviceKeys.PERIPHERAL_LIGHT20 ]; - public event NewLayerRendered? NewLayerRender = delegate { }; - - private Bitmap? _forcedFrame; + private event NewLayerRendered? _newLayerRender = delegate { }; + + public event NewLayerRendered? NewLayerRender + { + add + { + if (_newLayerRender?.GetInvocationList().Length < 2) + { + Background.ChangeToBitmapEffectLayer(); + } + _newLayerRender += value; // Update stored event listeners + } + remove + { + _newLayerRender -= value; // Update stored event listeners + if (_newLayerRender?.GetInvocationList().Length < 2) + { + Background.ChangeToNoRenderLayer(); + } + } + } public static event EventHandler? CanvasChanged; private static readonly object CanvasChangedLock = new(); @@ -136,16 +155,8 @@ public static EffectCanvas Canvas private readonly Dictionary _keyColors = new(MaxDeviceId, EnumHashGetter.Instance as IEqualityComparer); - private ReadableEffectLayer Background { get; } = new("Global Background", Color.Black); - - private readonly SingleColorBrush _keyboardDarknessBrush = new(); - private readonly SingleColorBrush _blackBrush = new(SimpleColor.Black); - - public void ForceImageRender(Bitmap? forcedFrame) - { - _forcedFrame?.Dispose(); - _forcedFrame = forcedFrame?.Clone() as Bitmap; - } + private RuntimeChangingLayer Background { get; } = new("Background Layer"); + private readonly Color _backgroundColor = Color.Black; public void PushFrame(EffectFrame frame) { @@ -159,7 +170,7 @@ public void PushFrame(EffectFrame frame) private void PushFrameLocked(EffectFrame frame) { - Background.Fill(_blackBrush); + Background.Fill(in _backgroundColor); var overLayersArray = frame.GetOverlayLayers(); var layersArray = frame.GetLayers(); @@ -169,17 +180,11 @@ private void PushFrameLocked(EffectFrame frame) foreach (var layer in overLayersArray) Background.Add(layer); - var keyboardDarkness = 1.0f - Global.Configuration.KeyboardBrightness * Global.Configuration.GlobalBrightness; - _keyboardDarknessBrush.Color = SimpleColor.FromRgba( 0, 0, 0, (byte) (255.0f * keyboardDarkness)); - Background.FillOver(_keyboardDarknessBrush); + var keyboardDarknessA = 1.0f - Global.Configuration.KeyboardBrightness * Global.Configuration.GlobalBrightness; + var keyboardDarkness = CommonColorUtils.FastColor(0, 0, 0, (byte) (255.0f * keyboardDarknessA)); + Background.FillOver(in keyboardDarkness); var renderCanvas = Canvas; // save locally in case it changes between ref calls - if (_forcedFrame != null) - { - var g = Background.GetGraphics(); - g.Fill(Brushes.Black); - g.DrawImage(_forcedFrame, 0, 0, renderCanvas.Width, renderCanvas.Height); - } foreach (var key in renderCanvas.BitmapMap.Keys) _keyColors[key] = (SimpleColor)Background.Get(key); @@ -196,7 +201,7 @@ private void PushFrameLocked(EffectFrame frame) deviceManager.Result.UpdateDevices(_keyColors); - NewLayerRender?.Invoke(Background.GetBitmap()); + _newLayerRender?.Invoke(Background.GetBitmap()); frame.Dispose(); } diff --git a/Project-Aurora/Project-Aurora/EffectsEngine/EmptyLayer.cs b/Project-Aurora/Project-Aurora/EffectsEngine/EmptyLayer.cs new file mode 100644 index 000000000..8fb805058 --- /dev/null +++ b/Project-Aurora/Project-Aurora/EffectsEngine/EmptyLayer.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; +using System.Drawing; +using AuroraRgb.Settings; +using Common.Devices; + +namespace AuroraRgb.EffectsEngine; + +public sealed class EmptyLayer : EffectLayer +{ + public static readonly EmptyLayer Instance = new(); + private static readonly Color TransparentColor = Color.Transparent; + + private EmptyLayer() + { + } + + public void Dispose() + { + } + + public DeviceKeys[] ActiveKeys { get; } = []; + + public void Fill(ref readonly Color color) + { + } + + public void FillOver(ref readonly Color color) + { + } + + public void Clear() + { + } + + public void Set(DeviceKeys key, ref readonly Color color) + { + } + + public void Set(DeviceKeys[] keys, ref readonly Color color) + { + } + + public void Set(KeySequence sequence, ref readonly Color color) + { + } + + public void PercentEffect(Color foregroundColor, Color backgroundColor, IReadOnlyList keys, double value, double total, + PercentEffectType percentEffectType = PercentEffectType.Progressive, double flashPast = 0, bool flashReversed = false, bool blinkBackground = false) + { + } + + public EffectLayer Add(EffectLayer other) + { + return other; + } + + public void Exclude(KeySequence sequence) + { + } + + public void OnlyInclude(KeySequence sequence) + { + } + + public void SetOpacity(double layerOpacity) + { + } + + public Color Get(DeviceKeys key) + { + return TransparentColor; + } + + public void Close() + { + } +} \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/EffectsEngine/IEffectLayer.cs b/Project-Aurora/Project-Aurora/EffectsEngine/IEffectLayer.cs new file mode 100644 index 000000000..c6316d412 --- /dev/null +++ b/Project-Aurora/Project-Aurora/EffectsEngine/IEffectLayer.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using AuroraRgb.Settings; +using Common.Devices; + +namespace AuroraRgb.EffectsEngine; + +public interface EffectLayer : IDisposable +{ + DeviceKeys[] ActiveKeys { get; } + + /// + /// Fills entire EffectLayer with a specified color. + /// + /// Color to be used during bitmap fill + void Fill(ref readonly Color color); + + /// + /// Paints over the entire bitmap of the EffectLayer with a specified color. + /// + /// Color to be used during bitmap fill + /// Itself + void FillOver(ref readonly Color color); + + void Clear(); + + /// + /// Sets a specific DeviceKeys on the bitmap with a specified color. + /// + /// DeviceKey to be set + /// Color to be used + /// Itself + void Set(DeviceKeys key, ref readonly Color color); + + /// + /// Sets a specific DeviceKeys on the bitmap with a specified color. + /// + /// Array of DeviceKeys to be set + /// Color to be used + void Set(DeviceKeys[] keys, ref readonly Color color); + + /// + /// Sets a specific KeySequence on the bitmap with a specified color. + /// + /// KeySequence to specify what regions of the bitmap need to be changed + /// Color to be used + /// Itself + void Set(KeySequence sequence, ref readonly Color color); + + void PercentEffect(Color foregroundColor, Color backgroundColor, IReadOnlyList keys, double value, + double total, PercentEffectType percentEffectType = PercentEffectType.Progressive, double flashPast = 0.0, + bool flashReversed = false, bool blinkBackground = false); + + /// + /// + Operator, sums two EffectLayer together. + /// + /// Left Hand Side EffectLayer + /// Right Hand Side EffectLayer + /// Left hand side EffectLayer, which is a combination of two passed EffectLayers + EffectLayer Add(EffectLayer other); + + /// + /// Excludes provided sequence from the layer (Applies a mask) + /// + /// The mask to be applied + void Exclude(KeySequence sequence); + + /// + /// Inlcudes provided sequence from the layer (Applies a mask) + /// + void OnlyInclude(KeySequence sequence); + + void SetOpacity(double layerOpacity); + + /// + /// Retrieves a color of the specified DeviceKeys key from the bitmap + /// + Color Get(DeviceKeys key); + + void Close(); +} \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/EffectsEngine/NoRenderLayer.cs b/Project-Aurora/Project-Aurora/EffectsEngine/NoRenderLayer.cs new file mode 100644 index 000000000..147c08173 --- /dev/null +++ b/Project-Aurora/Project-Aurora/EffectsEngine/NoRenderLayer.cs @@ -0,0 +1,273 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using AuroraRgb.Settings; +using AuroraRgb.Utils; +using Common.Devices; +using Common.Utils; + +namespace AuroraRgb.EffectsEngine; + +public sealed class NoRenderLayer : EffectLayer +{ + private readonly Color[] _keyColors = new Color[Effects.MaxDeviceId + 1]; + private readonly Color _transparent = Color.Transparent; + private double _opacity = 1; + private bool _isOpaque = true; + + private Color _lastColor = Color.Transparent; + private double _percentProgress = -1; + private Color _colorCache; + + // TODO optimize a lot by reducing the result of this + public DeviceKeys[] ActiveKeys => Effects.Canvas.Keys; + private DeviceKeys[] AllKeys => Effects.Canvas.Keys; + + public void Fill(ref readonly Color color) + { + // set all _keyColors to Transparent + foreach (var key in AllKeys) + Set(key, in color); + } + + public void FillOver(ref readonly Color color) + { + foreach (var deviceKey in AllKeys) + { + SetOver(deviceKey, in color); + } + } + + public void Clear() + { + Fill(Color.Transparent); + } + + public void Set(DeviceKeys key, ref readonly Color color) + { + if (key == DeviceKeys.NONE) + { + return; + } + + _keyColors[(int)key] = color; + } + + public void Set(DeviceKeys[] keys, ref readonly Color color) + { + foreach (var deviceKeys in keys) + { + Set(deviceKeys, in color); + } + } + + public void Set(IEnumerable keys, ref readonly Color color) + { + foreach (var deviceKeys in keys) + { + Set(deviceKeys, in color); + } + } + + public void Set(KeySequence sequence, ref readonly Color color) + { + var keys = GetKeys(sequence); + Set(keys, in color); + } + + private IEnumerable GetKeys(KeySequence sequence) + { + switch (sequence.Type) + { + case KeySequenceType.Sequence: + { + return sequence.Keys; + } + case KeySequenceType.FreeForm: + { + return GetKeys(sequence.Freeform); + } + } + + return []; + } + + private IEnumerable GetKeys(FreeFormObject sequenceFreeform) + { + //TODO implement getting keys inside the rectangle + return []; + } + + public EffectLayer Add(EffectLayer other) + { + if (other == EmptyLayer.Instance) + { + return this; + } + + // magic iteration from https://blog.ndepend.com/c-array-and-list-fastest-loop/ + Span span = other.ActiveKeys; + ref var start = ref MemoryMarshal.GetReference(span); + ref var end = ref Unsafe.Add(ref start, span.Length); + + while (Unsafe.IsAddressLessThan(ref start, ref end)) { + var foregroundColor = other.Get(start); + SetOver(start, in foregroundColor); + start = ref Unsafe.Add(ref start, 1); + } + + return this; + } + + private void SetOver(DeviceKeys key, ref readonly Color foregroundColor) + { + switch (foregroundColor.A) + { + // If the drawn color is fully opaque, draw it directly + case 255: + { + Set(key, in foregroundColor); + return; + } + // If the drawn color is fully transparent, do nothing + case 0: + { + return; + } + } + + var backgroundColor = Get(key); + ref var newColor = ref CommonColorUtils.AddColors(in backgroundColor, in foregroundColor, ref _colorCache); + Set(key, in newColor); + } + + /// + /// Draws a percent effect on the layer bitmap using an array of DeviceKeys keys and solid colors. + /// + /// The foreground color, used as a "Progress bar color" + /// The current progress value + /// The maxiumum progress value + public void PercentEffect(Color foregroundColor, Color backgroundColor, IReadOnlyList keys, double value, + double total, PercentEffectType percentEffectType = PercentEffectType.Progressive, double flashPast = 0.0, + bool flashReversed = false, bool blinkBackground = false) + { + var progressTotal = value / total; + if (progressTotal < 0.0) + progressTotal = 0.0; + else if (progressTotal > 1.0) + progressTotal = 1.0; + + var progress = progressTotal * keys.Count; + + if (flashPast > 0.0 && ((flashReversed && progressTotal >= flashPast) || (!flashReversed && progressTotal <= flashPast))) + { + var percent = Math.Sin(Time.GetMillisecondsSinceEpoch() % 1000.0D / 1000.0D * Math.PI); + if (blinkBackground) + backgroundColor = ColorUtils.BlendColors(backgroundColor, Color.Empty, percent); + else + foregroundColor = ColorUtils.BlendColors(backgroundColor, foregroundColor, percent); + } + + if (percentEffectType is PercentEffectType.Highest_Key or PercentEffectType.Highest_Key_Blend && keys.Count > 0) + { + var activeKey = (int)Math.Ceiling(Math.Clamp(value, 0, 1) / (total / keys.Count)) - 1; + var col = percentEffectType == PercentEffectType.Highest_Key ? + foregroundColor : ColorUtils.BlendColors(backgroundColor, foregroundColor, progressTotal); + for (var i = 0; i < keys.Count; i++) + { + if (i != activeKey) + { + Set(keys[i], Color.Transparent); + } + } + Set(keys[activeKey], in col); + + } + else + { + for (var i = 0; i < keys.Count; i++) + { + var currentKey = keys[i]; + + switch (percentEffectType) + { + case PercentEffectType.AllAtOnce: + Set(currentKey, ColorUtils.BlendColors(backgroundColor, foregroundColor, progressTotal)); + break; + case PercentEffectType.Progressive_Gradual: + if (i == (int)progress) + { + var percent = progress - i; + Set(currentKey, ColorUtils.BlendColors(backgroundColor, foregroundColor, percent)); + } + else if (i < (int)progress) + Set(currentKey, foregroundColor); + else + Set(currentKey, backgroundColor); + break; + default: + Set(currentKey, i < (int) progress ? foregroundColor : backgroundColor); + break; + } + } + } + } + + public void Exclude(KeySequence sequence) + { + Exclude(GetKeys(sequence)); + } + + private void Exclude(IEnumerable exclusion) + { + foreach (var deviceKey in exclusion) + { + Set(deviceKey, Color.Transparent); + } + } + + public void OnlyInclude(KeySequence sequence) + { + var exclusion = ActiveKeys.Except(GetKeys(sequence)); + Exclude(exclusion); + } + + public void SetOpacity(double layerOpacity) + { + _opacity = layerOpacity; + _isOpaque = _opacity > 0.999; + } + + public Color Get(DeviceKeys key) + { + if (_isOpaque) + { + return GetCurrentColor(key); + } + + var color = GetCurrentColor(key); + var a = (byte)(color.A * _opacity); + return CommonColorUtils.FastColor(color.R, color.G, color.B, a); + } + + public void Close() + { + } + + public void Dispose() + { + } + + private Color GetCurrentColor(DeviceKeys deviceKey) + { + if (deviceKey == DeviceKeys.NONE) + { + return _transparent; + } + + return _keyColors[(int)deviceKey]; + } +} \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/EffectsEngine/ReadableEffectLayer.cs b/Project-Aurora/Project-Aurora/EffectsEngine/ReadableEffectLayer.cs deleted file mode 100644 index bcf244bd7..000000000 --- a/Project-Aurora/Project-Aurora/EffectsEngine/ReadableEffectLayer.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Drawing; -using AuroraRgb.Bitmaps; -using Common.Devices; - -namespace AuroraRgb.EffectsEngine; - -public class ReadableEffectLayer(string name, Color color) : EffectLayer(name, color, LayerReadability.Readable) -{ - private IBitmapReader? _bitmapReader; - - /// - /// Retrieves a color of the specified DeviceKeys key from the bitmap - /// - public Color Get(DeviceKeys key) - { - var keyRectangle = Effects.Canvas.GetRectangle(key); - - var keyColor = keyRectangle.IsEmpty switch - { - true => Color.Black, - false => GetColor(keyRectangle.Rectangle) - }; - return keyColor; - } - - private Color GetColor(Rectangle rectangle) - { - _bitmapReader ??= _colormap.CreateReader(); - return _bitmapReader.GetRegionColor(rectangle); - } - - public void Close() - { - _bitmapReader?.Dispose(); - _bitmapReader = null; - } -} \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/EffectsEngine/RuntimeChangingLayer.cs b/Project-Aurora/Project-Aurora/EffectsEngine/RuntimeChangingLayer.cs new file mode 100644 index 000000000..a6729d80c --- /dev/null +++ b/Project-Aurora/Project-Aurora/EffectsEngine/RuntimeChangingLayer.cs @@ -0,0 +1,34 @@ +using AuroraRgb.Bitmaps; +using AuroraSourceGenerator; + +namespace AuroraRgb.EffectsEngine; + +[DelegateTo(nameof(_effectLayer))] +public sealed partial class RuntimeChangingLayer : EffectLayer +{ + private readonly NoRenderLayer _noRenderLayer = new(); + private readonly BitmapEffectLayer _bitmapEffectLayer; + + private EffectLayer _effectLayer; + + public RuntimeChangingLayer(string name) + { + _bitmapEffectLayer = new BitmapEffectLayer(name, true); + _effectLayer = _noRenderLayer; + } + + public void ChangeToNoRenderLayer() + { + _effectLayer = _noRenderLayer; + } + + public void ChangeToBitmapEffectLayer() + { + _effectLayer = _bitmapEffectLayer; + } + + public IAuroraBitmap GetBitmap() + { + return _bitmapEffectLayer.GetBitmap(); + } +} \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOBackgroundLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOBackgroundLayerHandler.cs index 4cee54934..d190d5fb4 100644 --- a/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOBackgroundLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOBackgroundLayerHandler.cs @@ -84,7 +84,8 @@ public class CSGOBackgroundLayerHandler() : LayerHandler -1 and < 100 || (csgostate.Round.WinTeam == RoundWinTeam.Undefined && csgostate.Previously?.Round.WinTeam != RoundWinTeam.Undefined); @@ -125,9 +126,9 @@ public override EffectLayer Render(IGameState gameState) } } - if (_solidBrush.Color == bgColor) return EffectLayer; - _solidBrush.Color = bgColor; - EffectLayer.Fill(_solidBrush); + if (_currentColor == bgColor) return EffectLayer; + _currentColor = bgColor; + EffectLayer.Fill(bgColor); return EffectLayer; } diff --git a/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOBombLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOBombLayerHandler.cs index 63015b4f4..2b506a4b6 100644 --- a/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOBombLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOBombLayerHandler.cs @@ -75,14 +75,14 @@ protected override UserControl CreateControl() public override EffectLayer Render(IGameState gameState) { - if (gameState is not GameStateCsgo csgostate) return EffectLayer.EmptyLayer; + if (gameState is not GameStateCsgo csgostate) return EmptyLayer.Instance; if (csgostate.Round.Bomb != BombState.Planted) { - if (!_bombTimer.IsRunning) return EffectLayer.EmptyLayer; + if (!_bombTimer.IsRunning) return EmptyLayer.Instance; Reset(); - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; } if (!_bombTimer.IsRunning) { @@ -129,12 +129,12 @@ public override EffectLayer Render(IGameState gameState) if (flashAmount < 0.01) { - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; } var bombColor = ColorUtils.MultiplyColorByScalar(isCritical ? Properties.PrimedColor : Properties.FlashColor, Math.Min(flashAmount, 1.0)); - EffectLayer.Set(Properties.Sequence, bombColor); + EffectLayer.Set(Properties.Sequence, in bombColor); return EffectLayer; } diff --git a/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOBurningLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOBurningLayerHandler.cs index d050bd5f4..d426b7e32 100644 --- a/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOBurningLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOBurningLayerHandler.cs @@ -41,7 +41,7 @@ public override void Default() public class CSGOBurningLayerHandler() : LayerHandler("CSGO - Burning") { private readonly Random _randomizer = new(); - private readonly SolidBrush _solidBrush = new(Color.Empty); + private Color _currentColor = Color.Transparent; protected override UserControl CreateControl() { @@ -50,11 +50,11 @@ protected override UserControl CreateControl() public override EffectLayer Render(IGameState gameState) { - if (gameState is not GameStateCsgo csgostate) return EffectLayer.EmptyLayer; + if (gameState is not GameStateCsgo csgostate) return EmptyLayer.Instance; //Update Burning - if (csgostate.Player.State.Burning <= 0) return EffectLayer.EmptyLayer; + if (csgostate.Player.State.Burning <= 0) return EmptyLayer.Instance; var burnColor = Properties.BurningColor; if (Properties.Animated) @@ -89,9 +89,9 @@ public override EffectLayer Render(IGameState gameState) burnColor = Color.FromArgb(csgostate.Player.State.Burning, red, green, blue); } - if (_solidBrush.Color == burnColor) return EffectLayer; - _solidBrush.Color = burnColor; - EffectLayer.Fill(_solidBrush); + if (_currentColor == burnColor) return EffectLayer; + _currentColor = burnColor; + EffectLayer.Fill(in _currentColor); return EffectLayer; } } \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGODeathLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGODeathLayerHandler.cs index 1297d6c80..bc9513cac 100644 --- a/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGODeathLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGODeathLayerHandler.cs @@ -45,7 +45,6 @@ public class CSGODeathLayerHandler() : LayerHandler 0) @@ -70,18 +69,18 @@ public override EffectLayer Render(IGameState state) if (!_isDead) { - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; } var fadeAlpha = GetFadeAlpha(); if (fadeAlpha <= 0) { _isDead = false; - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; } - _solidBrush.Color = CommonColorUtils.FastColor(deathColor.R, deathColor.G, deathColor.B, (byte)fadeAlpha); - EffectLayer.Fill(_solidBrush); + var color = CommonColorUtils.FastColor(deathColor.R, deathColor.G, deathColor.B, (byte)fadeAlpha); + EffectLayer.Fill(in color); return EffectLayer; } diff --git a/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOKillsIndicatorLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOKillsIndicatorLayerHandler.cs index b0e3c0bed..f4557f4d4 100644 --- a/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOKillsIndicatorLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOKillsIndicatorLayerHandler.cs @@ -73,7 +73,7 @@ protected override UserControl CreateControl() public override EffectLayer Render(IGameState gameState) { - if (gameState is not GameStateCsgo csgostate) return EffectLayer.EmptyLayer; + if (gameState is not GameStateCsgo csgostate) return EmptyLayer.Instance; if (!csgostate.Provider.SteamID.Equals(csgostate.Player.SteamID)) return EffectLayer; if (csgostate.Round.Phase == RoundPhase.FreezeTime) return EffectLayer; @@ -96,7 +96,7 @@ public override EffectLayer Render(IGameState gameState) EffectLayer.Set(Properties.Sequence.Keys[pos], Properties.HeadshotKillColor); break; case RoundKillType.None: - EffectLayer.Set(Properties.Sequence.Keys[pos], Color.Empty); + EffectLayer.Set(Properties.Sequence.Keys[pos], in Color.Empty); break; } } diff --git a/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOTypingIndicatorLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOTypingIndicatorLayerHandler.cs index 79afa78e4..e722a17e6 100644 --- a/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOTypingIndicatorLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOTypingIndicatorLayerHandler.cs @@ -47,8 +47,8 @@ protected override UserControl CreateControl() public override EffectLayer Render(IGameState gameState) { - if (gameState is not GameStateCsgo csgostate) return EffectLayer.EmptyLayer; - if (csgostate.Player.Activity != PlayerActivity.TextInput) return EffectLayer.EmptyLayer; + if (gameState is not GameStateCsgo csgostate) return EmptyLayer.Instance; + if (csgostate.Player.Activity != PlayerActivity.TextInput) return EmptyLayer.Instance; //Update Typing Keys EffectLayer.Set(Properties.Sequence, Properties.TypingKeysColor); diff --git a/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOWinningTeamLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOWinningTeamLayerHandler.cs index b7e5ccd6d..cd7c8ffde 100644 --- a/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOWinningTeamLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/CSGO/Layers/CSGOWinningTeamLayerHandler.cs @@ -36,24 +36,22 @@ public override void Default() public class CSGOWinningTeamLayerHandler() : LayerHandler("CSGO - Winning Team Effect") { - private readonly SolidBrush _solidBrush = new(Color.Empty); - protected override UserControl CreateControl() { return new Control_CSGOWinningTeamLayer(this); } - public override EffectLayer Render(IGameState state) + public override EffectLayer Render(IGameState gameState) { - if (state is not GameStateCsgo csgostate) return EffectLayer.EmptyLayer; + if (gameState is not GameStateCsgo csgostate) return EmptyLayer.Instance; // Block animations after end of round if (csgostate.Map.Phase == MapPhase.Undefined || csgostate.Round.Phase != RoundPhase.Over) { - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; } - _solidBrush.Color = Color.White; + var color = Color.White; // Triggers directly after a team wins a round if (csgostate.Round.WinTeam != RoundWinTeam.Undefined && csgostate.Previously?.Round.WinTeam == RoundWinTeam.Undefined) @@ -67,26 +65,26 @@ public override EffectLayer Render(IGameState state) if (tScore > ctScore) { - _solidBrush.Color = Properties.TColor; + color = Properties.TColor; } else if (ctScore > tScore) { - _solidBrush.Color = Properties.CtColor; + color = Properties.CtColor; } } else { - _solidBrush.Color = csgostate.Round.WinTeam switch + color = csgostate.Round.WinTeam switch { // End of round RoundWinTeam.T => Properties.TColor, RoundWinTeam.CT => Properties.CtColor, - _ => _solidBrush.Color + _ => color }; } } - EffectLayer.Fill(_solidBrush); + EffectLayer.Fill(in color); return EffectLayer; } diff --git a/Project-Aurora/Project-Aurora/Profiles/Desktop/DesktopProfile.cs b/Project-Aurora/Project-Aurora/Profiles/Desktop/DesktopProfile.cs index 990a4ff0f..a9d1a6413 100755 --- a/Project-Aurora/Project-Aurora/Profiles/Desktop/DesktopProfile.cs +++ b/Project-Aurora/Project-Aurora/Profiles/Desktop/DesktopProfile.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Collections.ObjectModel; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; @@ -41,10 +40,9 @@ private void AddVolumeOverlay() { Properties = new PercentGradientLayerHandlerProperties { - _Sequence = new KeySequence(new[] - { + _Sequence = new KeySequence([ DeviceKeys.VOLUME_MUTE, DeviceKeys.VOLUME_UP, DeviceKeys.VOLUME_DOWN - }), + ]), PercentType = PercentEffectType.AllAtOnce, Gradient = new EffectBrush(EffectBrush.BrushType.Linear) { @@ -65,15 +63,14 @@ private void AddVolumeOverlay() private void AddPause() { - OverlayLayers.Add(new Layer("Media Pause", new SolidColorLayerHandler() + OverlayLayers.Add(new Layer("Media Pause", new SolidColorLayerHandler { Properties = new LayerHandlerProperties { _PrimaryColor = Color.Yellow, - _Sequence = new KeySequence(new[] - { + _Sequence = new KeySequence([ DeviceKeys.MEDIA_PLAY, DeviceKeys.MEDIA_PLAY_PAUSE - }), + ]), }, }, new OverrideLogicBuilder().SetDynamicBoolean("_Enabled", new BooleanGSIBoolean("LocalPCInfo/Media/MediaPlaying") @@ -87,10 +84,9 @@ private void AddPlay() Properties = new LayerHandlerProperties { _PrimaryColor = Color.Lime, - _Sequence = new KeySequence(new[] - { + _Sequence = new KeySequence([ DeviceKeys.MEDIA_PLAY, DeviceKeys.MEDIA_PLAY_PAUSE - }), + ]), }, }, new OverrideLogicBuilder().SetDynamicBoolean("_Enabled", new BooleanAnd( @@ -107,10 +103,9 @@ private void AddNext() Properties = new LayerHandlerProperties { _PrimaryColor = Color.Lime, - _Sequence = new KeySequence(new[] - { + _Sequence = new KeySequence([ DeviceKeys.MEDIA_NEXT - }), + ]), }, }, new OverrideLogicBuilder().SetDynamicBoolean("_Enabled", new BooleanGSIBoolean("LocalPCInfo/Media/HasNextMedia") @@ -124,10 +119,9 @@ private void AddPrevious() Properties = new LayerHandlerProperties { _PrimaryColor = Color.Lime, - _Sequence = new KeySequence(new[] - { + _Sequence = new KeySequence([ DeviceKeys.MEDIA_PREVIOUS - }), + ]), }, }, new OverrideLogicBuilder().SetDynamicBoolean("_Enabled", new BooleanGSIBoolean("LocalPCInfo/Media/HasPreviousMedia") @@ -141,14 +135,13 @@ private void AddHasMedia() Properties = new LayerHandlerProperties { _PrimaryColor = Color.Red, - _Sequence = new KeySequence(new[] - { + _Sequence = new KeySequence([ DeviceKeys.VOLUME_MUTE, DeviceKeys.MEDIA_PREVIOUS, DeviceKeys.MEDIA_PLAY, DeviceKeys.MEDIA_PLAY_PAUSE, DeviceKeys.MEDIA_STOP, - DeviceKeys.MEDIA_NEXT, - }), + DeviceKeys.MEDIA_NEXT + ]), }, }, new OverrideLogicBuilder().SetDynamicBoolean("_Enabled", new BooleanGSIBoolean("LocalPCInfo/Media/HasMedia") @@ -178,8 +171,8 @@ public override void Reset() new NumberMathsOperation(new NumberGSINumeric("LocalPCInfo/Desktop/AccentB"), MathsOperator.Div, new NumberConstant(255)) ); - Layers = new ObservableCollection - { + Layers = + [ new("Num Lock", new LockColourLayerHandler { Properties = new LockColourLayerHandlerProperties @@ -187,9 +180,10 @@ public override void Reset() _ToggledKey = Keys.NumLock, _PrimaryColor = CommonColorUtils.FastColor(108, 20, 255), SecondaryColor = CommonColorUtils.FastColor(255, 0, 4, 120), - _Sequence = new KeySequence(new[] { DeviceKeys.NUM_LOCK }), + _Sequence = new KeySequence([DeviceKeys.NUM_LOCK]), } }), + new("Caps", new LockColourLayerHandler { Properties = new LockColourLayerHandlerProperties @@ -197,104 +191,107 @@ public override void Reset() _ToggledKey = Keys.Capital, _PrimaryColor = CommonColorUtils.FastColor(164, 45, 69), SecondaryColor = CommonColorUtils.FastColor(0, 0, 0, 0), - _Sequence = new KeySequence(new[] { DeviceKeys.CAPS_LOCK }), + _Sequence = new KeySequence([DeviceKeys.CAPS_LOCK]), } }), + new("Ctrl Shortcuts", new ShortcutAssistantLayerHandler { Properties = new ShortcutAssistantLayerHandlerProperties { _PrimaryColor = Color.Red, - ShortcutKeys = new Keybind[] - { - new(new[] { Keys.LControlKey, Keys.X }), - new(new[] { Keys.LControlKey, Keys.C }), - new(new[] { Keys.LControlKey, Keys.V }), - new(new[] { Keys.LControlKey, Keys.Z }), - new(new[] { Keys.LControlKey, Keys.F4 }), - new(new[] { Keys.LControlKey, Keys.A }), - new(new[] { Keys.LControlKey, Keys.D }), - new(new[] { Keys.LControlKey, Keys.R }), - new(new[] { Keys.LControlKey, Keys.Y }), - new(new[] { Keys.LControlKey, Keys.Right }), - new(new[] { Keys.LControlKey, Keys.Left }), - new(new[] { Keys.LControlKey, Keys.Down }), - new(new[] { Keys.LControlKey, Keys.Up }), - new(new[] { Keys.LControlKey, Keys.LMenu, Keys.Tab }), - new(new[] { Keys.LControlKey, Keys.LMenu, Keys.Delete }), - new(new[] { Keys.LControlKey, Keys.LShiftKey, Keys.Up }), - new(new[] { Keys.LControlKey, Keys.LShiftKey, Keys.Down }), - new(new[] { Keys.LControlKey, Keys.LShiftKey, Keys.Left }), - new(new[] { Keys.LControlKey, Keys.LShiftKey, Keys.Right }), - new(new[] { Keys.LControlKey, Keys.Escape }), - new(new[] { Keys.LControlKey, Keys.LShiftKey, Keys.Escape }), - new(new[] { Keys.LControlKey, Keys.Escape }), - new(new[] { Keys.LControlKey, Keys.F }) - }, + ShortcutKeys = + [ + new([Keys.LControlKey, Keys.X]), + new([Keys.LControlKey, Keys.C]), + new([Keys.LControlKey, Keys.V]), + new([Keys.LControlKey, Keys.Z]), + new([Keys.LControlKey, Keys.F4]), + new([Keys.LControlKey, Keys.A]), + new([Keys.LControlKey, Keys.D]), + new([Keys.LControlKey, Keys.R]), + new([Keys.LControlKey, Keys.Y]), + new([Keys.LControlKey, Keys.Right]), + new([Keys.LControlKey, Keys.Left]), + new([Keys.LControlKey, Keys.Down]), + new([Keys.LControlKey, Keys.Up]), + new([Keys.LControlKey, Keys.LMenu, Keys.Tab]), + new([Keys.LControlKey, Keys.LMenu, Keys.Delete]), + new([Keys.LControlKey, Keys.LShiftKey, Keys.Up]), + new([Keys.LControlKey, Keys.LShiftKey, Keys.Down]), + new([Keys.LControlKey, Keys.LShiftKey, Keys.Left]), + new([Keys.LControlKey, Keys.LShiftKey, Keys.Right]), + new([Keys.LControlKey, Keys.Escape]), + new([Keys.LControlKey, Keys.LShiftKey, Keys.Escape]), + new([Keys.LControlKey, Keys.Escape]), + new([Keys.LControlKey, Keys.F]) + ], PresentationType = ShortcutAssistantPresentationType.ProgressiveSuggestion, } }, accentColorOverride), + new("Win Shortcuts", new ShortcutAssistantLayerHandler { Properties = new ShortcutAssistantLayerHandlerProperties { _PrimaryColor = Color.Blue, - ShortcutKeys = new Keybind[] - { - new(new[] { Keys.LWin, Keys.L }), - new(new[] { Keys.LWin, Keys.D }), - new(new[] { Keys.LWin, Keys.B }), - new(new[] { Keys.LWin, Keys.A }), - new(new[] { Keys.LWin, Keys.LMenu, Keys.D }), - new(new[] { Keys.LWin, Keys.E }), - new(new[] { Keys.LWin, Keys.G }), - new(new[] { Keys.LWin, Keys.I }), - new(new[] { Keys.LWin, Keys.M }), - new(new[] { Keys.LWin, Keys.P }), - new(new[] { Keys.LWin, Keys.R }), - new(new[] { Keys.LWin, Keys.S }), - new(new[] { Keys.LWin, Keys.Up }), - new(new[] { Keys.LWin, Keys.Down }), - new(new[] { Keys.LWin, Keys.Left }), - new(new[] { Keys.LWin, Keys.Right }), - new(new[] { Keys.LWin, Keys.Home }), - new(new[] { Keys.LWin, Keys.D }) - }, + ShortcutKeys = + [ + new([Keys.LWin, Keys.L]), + new([Keys.LWin, Keys.D]), + new([Keys.LWin, Keys.B]), + new([Keys.LWin, Keys.A]), + new([Keys.LWin, Keys.LMenu, Keys.D]), + new([Keys.LWin, Keys.E]), + new([Keys.LWin, Keys.G]), + new([Keys.LWin, Keys.I]), + new([Keys.LWin, Keys.M]), + new([Keys.LWin, Keys.P]), + new([Keys.LWin, Keys.R]), + new([Keys.LWin, Keys.S]), + new([Keys.LWin, Keys.Up]), + new([Keys.LWin, Keys.Down]), + new([Keys.LWin, Keys.Left]), + new([Keys.LWin, Keys.Right]), + new([Keys.LWin, Keys.Home]), + new([Keys.LWin, Keys.D]) + ], PresentationType = ShortcutAssistantPresentationType.ProgressiveSuggestion, } }, accentColorOverride), + new("Alt Shortcuts", new ShortcutAssistantLayerHandler { Properties = new ShortcutAssistantLayerHandlerProperties { _PrimaryColor = Color.Yellow, - ShortcutKeys = new Keybind[] - { - new(new[] { Keys.LMenu, Keys.Tab }), - new(new[] { Keys.LMenu, Keys.F4 }), - new(new[] { Keys.LMenu, Keys.Space }), - new(new[] { Keys.LMenu, Keys.Left }), - new(new[] { Keys.LMenu, Keys.Right }), - new(new[] { Keys.LMenu, Keys.PageUp }), - new(new[] { Keys.LMenu, Keys.PageDown }), - new(new[] { Keys.LMenu, Keys.Tab }), - }, + ShortcutKeys = + [ + new([Keys.LMenu, Keys.Tab]), + new([Keys.LMenu, Keys.F4]), + new([Keys.LMenu, Keys.Space]), + new([Keys.LMenu, Keys.Left]), + new([Keys.LMenu, Keys.Right]), + new([Keys.LMenu, Keys.PageUp]), + new([Keys.LMenu, Keys.PageDown]), + new([Keys.LMenu, Keys.Tab]) + ], PresentationType = ShortcutAssistantPresentationType.ProgressiveSuggestion, } }, accentColorOverride), + new("Accent", new SolidColorLayerHandler { Properties = new LayerHandlerProperties { - _Sequence = new KeySequence(new[] - { + _Sequence = new KeySequence([ DeviceKeys.ESC, DeviceKeys.TILDE, DeviceKeys.TAB, DeviceKeys.CAPS_LOCK, DeviceKeys.LEFT_SHIFT, DeviceKeys.LEFT_CONTROL, DeviceKeys.LEFT_WINDOWS, DeviceKeys.LEFT_ALT, DeviceKeys.RIGHT_ALT, DeviceKeys.FN_Key, DeviceKeys.LEFT_FN, DeviceKeys.APPLICATION_SELECT, DeviceKeys.RIGHT_CONTROL, DeviceKeys.RIGHT_SHIFT, DeviceKeys.ENTER, DeviceKeys.BACKSPACE, DeviceKeys.NUM_LOCK_LED, DeviceKeys.CAPS_LOCK_LED, DeviceKeys.SCROLL_LOCK_LED - }) + ]) } }, accentColorOverride.SetLookupTable(nameof(LayerHandlerProperties._Enabled), @@ -302,8 +299,9 @@ public override void Reset() .AddEntry(false, new BooleanKeyDown(Keys.LControlKey)) .AddEntry(false, new BooleanKeyDown(Keys.LWin)) .AddEntry(false, new BooleanKeyDown(Keys.LMenu)) - ) - ), + ) + ), + new("CPU Usage", new PercentLayerHandler { Properties = new PercentLayerHandlerProperties @@ -311,12 +309,11 @@ public override void Reset() _PrimaryColor = Color.FromArgb(0, 205, 255), SecondaryColor = Color.FromArgb(0, 65, 80), PercentType = PercentEffectType.Progressive_Gradual, - _Sequence = new KeySequence(new[] - { + _Sequence = new KeySequence([ DeviceKeys.F1, DeviceKeys.F2, DeviceKeys.F3, DeviceKeys.F4, DeviceKeys.F5, DeviceKeys.F6, DeviceKeys.F7, DeviceKeys.F8, DeviceKeys.F9, DeviceKeys.F10, DeviceKeys.F11, DeviceKeys.F12 - }), + ]), BlinkThreshold = 0.0, BlinkDirection = false, VariablePath = new VariablePath("LocalPCInfo/CPU/Usage"), @@ -326,6 +323,7 @@ public override void Reset() { Enabled = false, }, + new("RAM Usage", new PercentLayerHandler { Properties = new PercentLayerHandlerProperties @@ -333,12 +331,11 @@ public override void Reset() _PrimaryColor = Color.FromArgb(255, 80, 0), SecondaryColor = Color.FromArgb(90, 30, 0), PercentType = PercentEffectType.Progressive_Gradual, - _Sequence = new KeySequence(new[] - { + _Sequence = new KeySequence([ DeviceKeys.ONE, DeviceKeys.TWO, DeviceKeys.THREE, DeviceKeys.FOUR, DeviceKeys.FIVE, DeviceKeys.SIX, DeviceKeys.SEVEN, DeviceKeys.EIGHT, DeviceKeys.NINE, DeviceKeys.ZERO, DeviceKeys.MINUS, DeviceKeys.EQUALS - }), + ]), BlinkThreshold = 0.0, BlinkDirection = false, VariablePath = new VariablePath("LocalPCInfo/RAM/Used"), @@ -348,6 +345,7 @@ public override void Reset() { Enabled = false, }, + new("Interactive Layer", new InteractiveLayerHandler { Properties = new InteractiveLayerHandlerProperties @@ -359,6 +357,7 @@ public override void Reset() EffectWidth = 4 } }), + new("Gradient Wave", new GradientLayerHandler { Properties = new GradientLayerHandlerProperties @@ -381,23 +380,24 @@ public override void Reset() End = new PointF(1, 1), ColorGradients = new SortedDictionary { - {0, CommonColorUtils.FastColor(0, 0, 0, 0) }, - {0.06593407690525055, CommonColorUtils.FastColor(0, 0, 0, 0) }, - {0.1538461595773697, CommonColorUtils.FastColor(153, 59, 237) }, - {0.24337075650691986, CommonColorUtils.FastColor(0, 0, 0, 0) }, - {0.4263019561767578, CommonColorUtils.FastColor(0, 0, 0, 0) }, - {0.5358933806419373, CommonColorUtils.FastColor(151, 183, 63) }, - {0.6483517289161682, CommonColorUtils.FastColor(0, 0, 0, 0) }, - {0.7614668011665344, CommonColorUtils.FastColor(0, 24, 24, 51) }, - {0.8626373410224915, CommonColorUtils.FastColor(129, 255, 239, 48) }, - {0.9395604133605957, CommonColorUtils.FastColor(0, 24, 24, 51) }, - {1, CommonColorUtils.FastColor(0, 0, 0, 0) }, + { 0, CommonColorUtils.FastColor(0, 0, 0, 0) }, + { 0.06593407690525055, CommonColorUtils.FastColor(0, 0, 0, 0) }, + { 0.1538461595773697, CommonColorUtils.FastColor(153, 59, 237) }, + { 0.24337075650691986, CommonColorUtils.FastColor(0, 0, 0, 0) }, + { 0.4263019561767578, CommonColorUtils.FastColor(0, 0, 0, 0) }, + { 0.5358933806419373, CommonColorUtils.FastColor(151, 183, 63) }, + { 0.6483517289161682, CommonColorUtils.FastColor(0, 0, 0, 0) }, + { 0.7614668011665344, CommonColorUtils.FastColor(0, 24, 24, 51) }, + { 0.8626373410224915, CommonColorUtils.FastColor(129, 255, 239) }, + { 0.9395604133605957, CommonColorUtils.FastColor(0, 24, 24, 51) }, + { 1, CommonColorUtils.FastColor(0, 0, 0, 0) }, }, }, }, - _LayerOpacity = 0.5f, + _LayerOpacity = 0.33f, } }), + new("Gradient Wave 2", new GradientLayerHandler { Properties = new GradientLayerHandlerProperties @@ -421,28 +421,38 @@ public override void Reset() End = new PointF(1, 1), ColorGradients = new SortedDictionary { - {0, CommonColorUtils.FastColor(0, 0, 0, 0) }, - {0.03296704590320587, CommonColorUtils.FastColor(0, 0, 0, 0) }, - {0.1538461595773697, CommonColorUtils.FastColor(153, 58, 249, 159) }, - {0.2983158230781555, CommonColorUtils.FastColor(10, 0, 0, 0) }, - {0.39333492517471313, CommonColorUtils.FastColor(0, 0, 0, 0) }, - {0.5358933806419373, CommonColorUtils.FastColor(151, 87, 63) }, - {0.6868132948875427, CommonColorUtils.FastColor(4, 0, 0, 0) }, - {0.7230052351951599, CommonColorUtils.FastColor(0, 24, 24, 51) }, - {0.8626373410224915, CommonColorUtils.FastColor(129, 255, 135, 48) }, - {0.9725274443626404, CommonColorUtils.FastColor(0, 24, 24, 51) }, - {1, CommonColorUtils.FastColor(0, 0, 0, 0) }, + { 0, CommonColorUtils.FastColor(0, 0, 0, 0) }, + { 0.03296704590320587, CommonColorUtils.FastColor(0, 0, 0, 0) }, + { 0.1538461595773697, CommonColorUtils.FastColor(153, 58, 249) }, + { 0.2983158230781555, CommonColorUtils.FastColor(10, 0, 0, 0) }, + { 0.39333492517471313, CommonColorUtils.FastColor(0, 0, 0, 0) }, + { 0.5358933806419373, CommonColorUtils.FastColor(151, 87, 63) }, + { 0.6868132948875427, CommonColorUtils.FastColor(4, 0, 0, 0) }, + { 0.7230052351951599, CommonColorUtils.FastColor(0, 24, 24, 51) }, + { 0.8626373410224915, CommonColorUtils.FastColor(129, 255, 135) }, + { 0.9725274443626404, CommonColorUtils.FastColor(0, 24, 24, 51) }, + { 1, CommonColorUtils.FastColor(0, 0, 0, 0) }, }, }, }, - _LayerOpacity = 0.5f, + _LayerOpacity = 0.33f, } }), + + new("Brightness", new SolidFillLayerHandler + { + Properties = new SolidFillLayerHandlerProperties + { + _PrimaryColor = Color.FromArgb(255, 255, 255, 255), + _LayerOpacity = 0.1f, + } + }), + new("Background", new GradientLayerHandler { Properties = new GradientLayerHandlerProperties { - _LayerOpacity = 0.08f, + _LayerOpacity = 0.20f, Sequence = { Freeform = new FreeFormObject(0, 0, 1200, 260), @@ -467,7 +477,7 @@ public override void Reset() } } } - }), - }; + }) + ]; } } \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/Profiles/Desktop/Event_Idle.cs b/Project-Aurora/Project-Aurora/Profiles/Desktop/Event_Idle.cs index 462b2ca6c..3894c3eb5 100644 --- a/Project-Aurora/Project-Aurora/Profiles/Desktop/Event_Idle.cs +++ b/Project-Aurora/Project-Aurora/Profiles/Desktop/Event_Idle.cs @@ -13,7 +13,7 @@ namespace AuroraRgb.Profiles.Desktop; public sealed class EventIdle : LightEvent { - private readonly EffectLayer _layer = new("IDLE", true); + private readonly BitmapEffectLayer _layer = new("IDLE", true); private DateTime _previousTime = DateTime.UtcNow; internal DateTime CurrentTime = DateTime.UtcNow; @@ -103,12 +103,12 @@ public abstract class AwayEffect protected readonly Color IdleEffectPrimaryColor = Global.Configuration.IdleEffectPrimaryColor; protected readonly SolidBrush IdEffectSecondaryColorBrush = new(Global.Configuration.IdleEffectSecondaryColor); - public abstract void Update(EffectLayer layer); + public abstract void Update(BitmapEffectLayer layer); } internal class NoneEffect : AwayEffect { - public override void Update(EffectLayer layer) + public override void Update(BitmapEffectLayer layer) { //noop } @@ -123,7 +123,7 @@ private DimEffect() { } - public override void Update(EffectLayer layer) + public override void Update(BitmapEffectLayer layer) { layer.Fill(_dimBrush); } @@ -131,7 +131,7 @@ public override void Update(EffectLayer layer) internal class ColorBreathingEffect(EventIdle eventIdle) : AwayEffect { - public override void Update(EffectLayer layer) + public override void Update(BitmapEffectLayer layer) { layer.Fill(IdEffectSecondaryColorBrush); var sine = (float) Math.Pow( @@ -143,7 +143,7 @@ public override void Update(EffectLayer layer) internal class RainbowShiftHorizontal(EventIdle eventIdle) : AwayEffect { - public override void Update(EffectLayer layer) + public override void Update(BitmapEffectLayer layer) { layer.DrawGradient(LayerEffects.RainbowShift_Horizontal, eventIdle.EffectCfg); } @@ -151,7 +151,7 @@ public override void Update(EffectLayer layer) internal class RainbowShiftVertical(EventIdle eventIdle) : AwayEffect { - public override void Update(EffectLayer layer) + public override void Update(BitmapEffectLayer layer) { layer.DrawGradient(LayerEffects.RainbowShift_Vertical, eventIdle.EffectCfg); } @@ -163,7 +163,7 @@ internal class StarFall(EventIdle eventIdle) : AwayEffect private readonly Dictionary _stars = new(); - public override void Update(EffectLayer layer) + public override void Update(BitmapEffectLayer layer) { if (_nextStarSet < eventIdle.CurrentTime) { @@ -198,7 +198,7 @@ internal class RainFall(EventIdle eventIdle) : AwayEffect private readonly Pen _pen = new(Color.Transparent, 2); - public override void Update(EffectLayer layer) + public override void Update(BitmapEffectLayer layer) { if (_nextStarSet < eventIdle.CurrentTime) { @@ -235,7 +235,7 @@ public override void Update(EffectLayer layer) internal class Blackout : AwayEffect { - public override void Update(EffectLayer layer) + public override void Update(BitmapEffectLayer layer) { layer.Fill(Brushes.Black); } @@ -246,7 +246,7 @@ public class Matrix(EventIdle eventIdle) : AwayEffect private readonly AnimationMix _matrixLines = new AnimationMix().SetAutoRemove(true); //This will be an infinite Mix private DateTime _nextStarSet; - public override void Update(EffectLayer layer) + public override void Update(BitmapEffectLayer layer) { var span = eventIdle.CurrentTime - DateTime.UnixEpoch; var ms = (long)span.TotalMilliseconds; @@ -312,7 +312,7 @@ internal class RainFallSmooth(EventIdle eventIdle) : AwayEffect private DateTime _nextStarSet; - public override void Update(EffectLayer layer) + public override void Update(BitmapEffectLayer layer) { if (_nextStarSet < eventIdle.CurrentTime) { diff --git a/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2AbilityLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2AbilityLayerHandler.cs index a5dd4ce7b..4409ca50f 100644 --- a/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2AbilityLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2AbilityLayerHandler.cs @@ -59,10 +59,10 @@ protected override UserControl CreateControl() public override EffectLayer Render(IGameState state) { - if (state is not GameStateDota2 dota2State) return EffectLayer.EmptyLayer; + if (state is not GameStateDota2 dota2State) return EmptyLayer.Instance; if (dota2State.Map.GameState != DOTA_GameState.DOTA_GAMERULES_STATE_PRE_GAME && dota2State.Map.GameState != DOTA_GameState.DOTA_GAMERULES_STATE_GAME_IN_PROGRESS) - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; for (var index = 0; index < dota2State.Abilities.Count; index++) { var ability = dota2State.Abilities[index]; diff --git a/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2BackgroundLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2BackgroundLayerHandler.cs index 39c62af6f..7b843eaae 100644 --- a/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2BackgroundLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2BackgroundLayerHandler.cs @@ -80,10 +80,10 @@ protected override UserControl CreateControl() return new Control_Dota2BackgroundLayer(this); } - private SolidBrush _currentColor = new(Color.Empty); + private Color _currentColor = Color.Transparent; public override EffectLayer Render(IGameState state) { - if (state is not GameStateDota2 dota2State) return EffectLayer.EmptyLayer; + if (state is not GameStateDota2 dota2State) return EmptyLayer.Instance; if (dota2State.Previously?.Hero.HealthPercent == 0 && dota2State.Hero.HealthPercent == 100 && !dota2State.Previously.Hero.IsAlive && dota2State.Hero.IsAlive) { @@ -113,11 +113,11 @@ public override EffectLayer Render(IGameState state) } } - if (!Invalidated && _currentColor.Color == bgColor) return EffectLayer; + if (!Invalidated && _currentColor == bgColor) return EffectLayer; - _currentColor = new SolidBrush(bgColor); + _currentColor = bgColor; EffectLayer.Clear(); - EffectLayer.Fill(_currentColor); + EffectLayer.Fill(in _currentColor); Invalidated = false; return EffectLayer; diff --git a/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2HeroAbiltiyEffectsLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2HeroAbiltiyEffectsLayerHandler.cs index 0d50baa0f..5eacae1f6 100644 --- a/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2HeroAbiltiyEffectsLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2HeroAbiltiyEffectsLayerHandler.cs @@ -12,7 +12,7 @@ namespace AuroraRgb.Profiles.Dota_2.Layers { - public class Dota2HeroAbilityEffectsLayerHandler : LayerHandler + public class Dota2HeroAbilityEffectsLayerHandler : LayerHandler { private enum Dota2AbilityEffects { @@ -131,7 +131,7 @@ public override EffectLayer Render(IGameState state) _previousTime = _currentTime; _currentTime = Time.GetMillisecondsSinceEpoch(); - if (state is not GameStateDota2 dota2State) return EffectLayer.EmptyLayer; + if (state is not GameStateDota2 dota2State) return EmptyLayer.Instance; //Preparations if (_abilities != null && dota2State.Abilities.Count == _abilities.Count) diff --git a/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2ItemLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2ItemLayerHandler.cs index 14624e003..e85ec7ead 100644 --- a/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2ItemLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2ItemLayerHandler.cs @@ -419,23 +419,23 @@ public override EffectLayer Render(IGameState state) { itemColor = Properties.ItemsColor; } - EffectLayer.Set(key, itemColor); + EffectLayer.Set(key, in itemColor); //Cooldown if (item.Cooldown > 5) - EffectLayer.Set(key, ColorUtils.BlendColors(itemColor, Properties.ItemCooldownColor, 1.0)); + EffectLayer.Set(key, ColorUtils.BlendColors(in itemColor, Properties.ItemCooldownColor, 1.0)); else if (item.Cooldown is > 0 and <= 5) - EffectLayer.Set(key, ColorUtils.BlendColors(itemColor, Properties.ItemCooldownColor, item.Cooldown / 5.0)); + EffectLayer.Set(key, ColorUtils.BlendColors(in itemColor, Properties.ItemCooldownColor, item.Cooldown / 5.0)); //Charges if (item.Charges == 0) - EffectLayer.Set(key, ColorUtils.BlendColors(itemColor, Properties.ItemNoChargersColor, 0.7)); + EffectLayer.Set(key, ColorUtils.BlendColors(in itemColor, Properties.ItemNoChargersColor, 0.7)); } } } else { - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; } return EffectLayer; diff --git a/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2KillstreakLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2KillstreakLayerHandler.cs index 2c2d05cb4..9731c2a99 100644 --- a/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2KillstreakLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2KillstreakLayerHandler.cs @@ -124,8 +124,6 @@ public class Dota2KillstreakLayerHandler() : LayerHandler 0)) return EffectLayer.EmptyLayer; + if (!(ksEffectValue > 0)) return EmptyLayer.Instance; EffectLayer.Clear(); - _solidBrush.Color = ColorUtils.BlendColors(Color.Transparent, ksColor, ksEffectValue); - EffectLayer.Fill(_solidBrush); + var color = ColorUtils.BlendColors(Color.Transparent, ksColor, ksEffectValue); + EffectLayer.Fill(in color); _empty = false; return EffectLayer; diff --git a/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2RespawnLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2RespawnLayerHandler.cs index 967747f8d..133a41f08 100644 --- a/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2RespawnLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/Dota 2/Layers/Dota2RespawnLayerHandler.cs @@ -57,7 +57,7 @@ public override void Default() } -public class Dota2RespawnLayerHandler() : LayerHandler("Dota 2 - Respawn") +public class Dota2RespawnLayerHandler() : LayerHandler("Dota 2 - Respawn") { private readonly SolidBrush _solidBrush = new(Color.Empty); @@ -66,14 +66,14 @@ protected override UserControl CreateControl() return new Control_Dota2RespawnLayer(this); } - public override EffectLayer Render(IGameState state) + public override EffectLayer Render(IGameState gameState) { - if (state is not GameStateDota2 dota2State) return EffectLayer.EmptyLayer; + if (gameState is not GameStateDota2 dota2State) return EmptyLayer.Instance; if (dota2State.Player.Team is DotaPlayerTeam.Undefined or DotaPlayerTeam.None || - dota2State.Hero.IsAlive) return EffectLayer.EmptyLayer; + dota2State.Hero.IsAlive) return EmptyLayer.Instance; var percent = dota2State.Hero.SecondsToRespawn > 5 ? 0.0 : 1.0 - dota2State.Hero.SecondsToRespawn / 5.0; - if (percent <= 0) return EffectLayer.EmptyLayer; + if (percent <= 0) return EmptyLayer.Instance; _solidBrush.Color = ColorUtils.BlendColors(Color.Transparent, Properties.BackgroundColor, percent); EffectLayer.Fill(_solidBrush); diff --git a/Project-Aurora/Project-Aurora/Profiles/ETS2/Layers/ETS2BeaconLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/ETS2/Layers/ETS2BeaconLayerHandler.cs index 90824125f..0c1be5338 100644 --- a/Project-Aurora/Project-Aurora/Profiles/ETS2/Layers/ETS2BeaconLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/ETS2/Layers/ETS2BeaconLayerHandler.cs @@ -52,7 +52,7 @@ public override void Default() { } } -public class ETS2BeaconLayerHandler : LayerHandler { +public class ETS2BeaconLayerHandler() : LayerHandler("ETS2BeaconLayerHandler") { private int _frame; @@ -67,7 +67,8 @@ private Color PrimaryColorAlpha(double a) { } public override EffectLayer Render(IGameState gamestate) { - var layer = new EffectLayer("ETS2 Beacon Layer"); + var layer = EffectLayer; + layer.Clear(); if (gamestate is GameState_ETS2 { Truck.lightsBeaconOn: true }) { switch (Properties.BeaconStyle) { diff --git a/Project-Aurora/Project-Aurora/Profiles/ETS2/Layers/ETS2BlinkerLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/ETS2/Layers/ETS2BlinkerLayerHandler.cs index 0e4e89806..0b174644f 100644 --- a/Project-Aurora/Project-Aurora/Profiles/ETS2/Layers/ETS2BlinkerLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/ETS2/Layers/ETS2BlinkerLayerHandler.cs @@ -52,22 +52,21 @@ public override void Default() { } } - public class ETS2BlinkerLayerHandler : LayerHandler { + public class ETS2BlinkerLayerHandler() : LayerHandler("Ets2BlinkerLayerHandler") { protected override UserControl CreateControl() { return new Control_ETS2BlinkerLayer(this); } public override EffectLayer Render(IGameState gamestate) { - var blinker_layer = new EffectLayer("ETS2 - Blinker Layer"); - if (gamestate is not GameState_ETS2 stateEts2) return blinker_layer; + if (gamestate is not GameState_ETS2 stateEts2) return EmptyLayer.Instance; // Left blinker var trgColor = stateEts2.Truck.blinkerLeftOn ? Properties.BlinkerOnColor : Properties.BlinkerOffColor; - blinker_layer.Set(Properties.LeftBlinkerSequence, trgColor); + EffectLayer.Set(Properties.LeftBlinkerSequence, trgColor); // Right blinker trgColor = stateEts2.Truck.blinkerRightOn ? Properties.BlinkerOnColor : Properties.BlinkerOffColor; - blinker_layer.Set(Properties.RightBlinkerSequence, trgColor); - return blinker_layer; + EffectLayer.Set(Properties.RightBlinkerSequence, trgColor); + return EffectLayer; } } } diff --git a/Project-Aurora/Project-Aurora/Profiles/EliteDangerous/Layers/EliteDangerousAnimationLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/EliteDangerous/Layers/EliteDangerousAnimationLayerHandler.cs index 7e4f806e5..c10503681 100644 --- a/Project-Aurora/Project-Aurora/Profiles/EliteDangerous/Layers/EliteDangerousAnimationLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/EliteDangerous/Layers/EliteDangerousAnimationLayerHandler.cs @@ -19,7 +19,7 @@ public enum EliteAnimation HyperspaceExit, } -public class EliteDangerousAnimationLayerHandler : LayerHandler +public class EliteDangerousAnimationLayerHandler : LayerHandler { private AnimationMix _fsdCountdownMix; private AnimationMix _hyperspaceMix; diff --git a/Project-Aurora/Project-Aurora/Profiles/EliteDangerous/Layers/EliteDangerousBackgroundLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/EliteDangerous/Layers/EliteDangerousBackgroundLayerHandler.cs index fd5cbc5c7..5ca4ec8ff 100644 --- a/Project-Aurora/Project-Aurora/Profiles/EliteDangerous/Layers/EliteDangerousBackgroundLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/EliteDangerous/Layers/EliteDangerousBackgroundLayerHandler.cs @@ -37,8 +37,6 @@ public override void Default() } public class EliteDangerousBackgroundLayerHandler() : LayerHandler("Elite: Dangerous - Background") { - private readonly SolidBrush _bg = new(Color.Transparent); - protected override UserControl CreateControl() { return new Control_EliteDangerousBackgroundLayer(this); @@ -48,8 +46,8 @@ public override EffectLayer Render(IGameState state) { var gameState = state as GameState_EliteDangerous; - _bg.Color = gameState.Status.IsFlagSet(Flag.HUD_DISCOVERY_MODE) ? Properties.DiscoveryModeColor : Properties.CombatModeColor; - EffectLayer.FillOver(_bg); + var color = gameState.Status.IsFlagSet(Flag.HUD_DISCOVERY_MODE) ? Properties.DiscoveryModeColor : Properties.CombatModeColor; + EffectLayer.FillOver(color); return EffectLayer; } diff --git a/Project-Aurora/Project-Aurora/Profiles/Guild Wars 2/Control_GW2.xaml.cs b/Project-Aurora/Project-Aurora/Profiles/Guild Wars 2/Control_GW2.xaml.cs index 52186533e..61a4e7229 100644 --- a/Project-Aurora/Project-Aurora/Profiles/Guild Wars 2/Control_GW2.xaml.cs +++ b/Project-Aurora/Project-Aurora/Profiles/Guild Wars 2/Control_GW2.xaml.cs @@ -1,5 +1,4 @@ -using System; -using System.IO; +using System.IO; using System.Windows; using System.Windows.Forms; using MessageBox = System.Windows.MessageBox; diff --git a/Project-Aurora/Project-Aurora/Profiles/LightingStateManager.cs b/Project-Aurora/Project-Aurora/Profiles/LightingStateManager.cs index 153e9c22e..3b0af5615 100755 --- a/Project-Aurora/Project-Aurora/Profiles/LightingStateManager.cs +++ b/Project-Aurora/Project-Aurora/Profiles/LightingStateManager.cs @@ -338,7 +338,7 @@ public bool RegisterLayer() where T : ILayerHandler return Events[value]; } - private SingleConcurrentThread _updateTimer; + private readonly SingleConcurrentThread _updateTimer; private long _nextProcessNameUpdate; private long _currentTick; diff --git a/Project-Aurora/Project-Aurora/Profiles/Minecraft/Layers/MinecraftBurnLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/Minecraft/Layers/MinecraftBurnLayerHandler.cs index 11f31f96f..b60af047e 100644 --- a/Project-Aurora/Project-Aurora/Profiles/Minecraft/Layers/MinecraftBurnLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/Minecraft/Layers/MinecraftBurnLayerHandler.cs @@ -10,7 +10,7 @@ namespace AuroraRgb.Profiles.Minecraft.Layers; [Obsolete("This layer is obselete and has been replaced by the Overrides system.")] -public class MinecraftBurnLayerHandler : LayerHandler { +public class MinecraftBurnLayerHandler : LayerHandler { private List particles = new(); private Random rnd = new(); @@ -40,7 +40,7 @@ private void CreateFireParticle() { public override EffectLayer Render(IGameState gamestate) { // Render nothing if invalid gamestate or player isn't on fire if (gamestate is not GameStateMinecraft minecraft || !minecraft.Player.IsBurning) - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; // Set the background to red EffectLayer.FillOver(Brushes.Red); diff --git a/Project-Aurora/Project-Aurora/Profiles/Minecraft/Layers/MinecraftHealthBarLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/Minecraft/Layers/MinecraftHealthBarLayerHandler.cs index 18a006042..0a7060149 100644 --- a/Project-Aurora/Project-Aurora/Profiles/Minecraft/Layers/MinecraftHealthBarLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/Minecraft/Layers/MinecraftHealthBarLayerHandler.cs @@ -64,19 +64,15 @@ public override void Default() { } } - public class MinecraftHealthBarLayerHandler : LayerHandler + public class MinecraftHealthBarLayerHandler() : LayerHandler("Minecraft Health Bar Layer") { - public MinecraftHealthBarLayerHandler() : base("Minecraft Health Bar Layer") - { - } - protected override UserControl CreateControl() { return new Control_MinecraftHealthBarLayer(this); } public override EffectLayer Render(IGameState gamestate) { // Ensure the gamestate is for Minecraft, and store a casted reference to it - if (gamestate is not GameStateMinecraft minecraftState) return EffectLayer.EmptyLayer; + if (gamestate is not GameStateMinecraft minecraftState) return EmptyLayer.Instance; // Choose the main healthbar's color depending on whether the player is withered/poisoned/regen/normal. var barColor = Properties.NormalHealthColor; // Default normal color diff --git a/Project-Aurora/Project-Aurora/Profiles/Minecraft/Layers/MinecraftKeyConflictLayer.cs b/Project-Aurora/Project-Aurora/Profiles/Minecraft/Layers/MinecraftKeyConflictLayer.cs index 4a240a7d8..dfc951316 100644 --- a/Project-Aurora/Project-Aurora/Profiles/Minecraft/Layers/MinecraftKeyConflictLayer.cs +++ b/Project-Aurora/Project-Aurora/Profiles/Minecraft/Layers/MinecraftKeyConflictLayer.cs @@ -28,13 +28,8 @@ public override void Default() { } - public class MinecraftKeyConflictLayerHandler : LayerHandler { - private readonly SolidBrush _backgroundBrush = new(Color.Black); - - public MinecraftKeyConflictLayerHandler() : base("Minecraft Key Conflict Layer") - { - } - + public class MinecraftKeyConflictLayerHandler() : LayerHandler("Minecraft Key Conflict Layer") + { protected override UserControl CreateControl() { return new Control_MinecraftKeyConflictLayer(this); } @@ -42,10 +37,10 @@ protected override UserControl CreateControl() { public override EffectLayer Render(IGameState gameState) { if (gameState is not GameStateMinecraft minecraftState || !minecraftState.Game.ControlsGuiOpen) { - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; } - EffectLayer.Fill(_backgroundBrush); // Hide any other layers behind this one + EffectLayer.Fill(Color.Black); // Hide any other layers behind this one // Set all keys in use by any binding to be the no-conflict colour foreach (var kb in minecraftState.Game.KeyBindings) if(kb!=null) diff --git a/Project-Aurora/Project-Aurora/Profiles/Minecraft/Layers/MinecraftRainLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/Minecraft/Layers/MinecraftRainLayerHandler.cs index d9122a8f2..3acb9634c 100644 --- a/Project-Aurora/Project-Aurora/Profiles/Minecraft/Layers/MinecraftRainLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/Minecraft/Layers/MinecraftRainLayerHandler.cs @@ -29,62 +29,58 @@ public override void Default() { } } -public class MinecraftRainLayerHandler : LayerHandler { - - private List raindrops = new(); - private Random rnd = new(); - private int frame; - - public MinecraftRainLayerHandler() : base("Minecraft Rain Layer") - { - } +public class MinecraftRainLayerHandler() : LayerHandler("Minecraft Rain Layer") +{ + private readonly List _raindrops = new(); + private readonly Random _rnd = new(); + private int _frame; protected override UserControl CreateControl() { return new Control_MinecraftRainLayer(this); } private void CreateRainDrop() { - var randomX = (float)rnd.NextDouble() * Effects.Canvas.Width; - raindrops.Add(new Droplet() { - mix = new AnimationMix(new[] { + var randomX = (float)_rnd.NextDouble() * Effects.Canvas.Width; + _raindrops.Add(new Droplet( + new AnimationMix([ new AnimationTrack("raindrop", 0) .SetFrame(0, new AnimationFilledRectangle(randomX, 0, 3, 6, Properties.PrimaryColor)) .SetFrame(1, new AnimationFilledRectangle(randomX + 5, Effects.Canvas.Height, 2, 4, Properties.PrimaryColor)) - }), - time = 0 - }); + ]) + )); } public override EffectLayer Render(IGameState gamestate) { - if (!(gamestate is GameStateMinecraft)) return EffectLayer.EmptyLayer; + if (gamestate is not GameStateMinecraft minecraft) return EmptyLayer.Instance; // Add more droplets based on the intensity - float strength = (gamestate as GameStateMinecraft).World.RainStrength; + float strength = minecraft.World.RainStrength; if (strength > 0) { - if (frame <= 0) { + if (_frame <= 0) { // calculate time (in frames) until next droplet is created float min = Properties.MinimumInterval, max = Properties.MaximumInterval; // Store as floats so C# doesn't prematurely round numbers - frame = (int)Math.Round(min - (min - max) * strength); // https://www.desmos.com/calculator/uak73e5eub + _frame = (int)Math.Round(min - (min - max) * strength); // https://www.desmos.com/calculator/uak73e5eub CreateRainDrop(); } else - frame--; + _frame--; } // Render all droplets var graphics = EffectLayer.GetGraphics(); - foreach (var droplet in raindrops) { - droplet.mix.Draw(graphics, droplet.time); - droplet.time += .1f; + foreach (var droplet in _raindrops) { + droplet.Mix.Draw(graphics, droplet.Time); + droplet.Time += .1f; } // Remove any expired droplets - raindrops.RemoveAll(droplet => droplet.time >= 1); + _raindrops.RemoveAll(droplet => droplet.Time >= 1); return EffectLayer; } } -internal class Droplet { - internal AnimationMix mix; - internal float time; +internal class Droplet(AnimationMix mix) +{ + internal AnimationMix Mix = mix; + internal float Time; } \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/Profiles/Payday 2/Layers/PD2BackgroundLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/Payday 2/Layers/PD2BackgroundLayerHandler.cs index e55442919..4e7b16077 100644 --- a/Project-Aurora/Project-Aurora/Profiles/Payday 2/Layers/PD2BackgroundLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/Payday 2/Layers/PD2BackgroundLayerHandler.cs @@ -93,7 +93,7 @@ public override void Default() } } -public class PD2BackgroundLayerHandler : LayerHandler +public class PD2BackgroundLayerHandler : LayerHandler { private float _noReturnFlashamount = 1.0f; private float _noReturnTimeleft; @@ -105,7 +105,7 @@ protected override UserControl CreateControl() public override EffectLayer Render(IGameState state) { - if (state is not GameState_PD2 pd2) return EffectLayer.EmptyLayer; + if (state is not GameState_PD2 pd2) return EmptyLayer.Instance; var bgColor = Properties.AmbientColor; var currenttime = Time.GetMillisecondsSinceEpoch(); @@ -174,10 +174,10 @@ public override EffectLayer Render(IGameState state) */ } - EffectLayer.FillOver(bgColor); + EffectLayer.FillOver(in bgColor); if (Properties.PeripheralUse) - EffectLayer.Set(DeviceKeys.Peripheral, bgColor); + EffectLayer.Set(DeviceKeys.Peripheral, in bgColor); } else if (pd2.Level.Phase == LevelPhase.Stealth && pd2.Game.State == GameStates.Ingame) { @@ -211,17 +211,17 @@ public override EffectLayer Render(IGameState state) if (_noReturnFlashamount < 0.0f) _noReturnFlashamount = 0.0f; - EffectLayer.FillOver(noReturnColor); + EffectLayer.FillOver(in noReturnColor); if (Properties.PeripheralUse) - EffectLayer.Set(DeviceKeys.Peripheral, noReturnColor); + EffectLayer.Set(DeviceKeys.Peripheral, in noReturnColor); } else { - EffectLayer.FillOver(bgColor); + EffectLayer.FillOver(in bgColor); if (Properties.PeripheralUse) - EffectLayer.Set(DeviceKeys.Peripheral, bgColor); + EffectLayer.Set(DeviceKeys.Peripheral, in bgColor); } return EffectLayer; diff --git a/Project-Aurora/Project-Aurora/Profiles/Payday 2/Layers/PD2FlashbangLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/Payday 2/Layers/PD2FlashbangLayerHandler.cs index b45da6c8a..3ac34488a 100644 --- a/Project-Aurora/Project-Aurora/Profiles/Payday 2/Layers/PD2FlashbangLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/Payday 2/Layers/PD2FlashbangLayerHandler.cs @@ -29,7 +29,7 @@ public override void Default() } -public class PD2FlashbangLayerHandler : LayerHandler +public class PD2FlashbangLayerHandler() : LayerHandler("PD2FlashbangLayerHandler") { protected override UserControl CreateControl() { @@ -38,13 +38,13 @@ protected override UserControl CreateControl() public override EffectLayer Render(IGameState gameState) { - if (gameState is not GameState_PD2 pd2State) return EffectLayer.EmptyLayer; + if (gameState is not GameState_PD2 pd2State) return EmptyLayer.Instance; //Update Flashed - if (pd2State.Game.State != GameStates.Ingame || pd2State.Players.LocalPlayer.FlashAmount <= 0) return EffectLayer.EmptyLayer; + if (pd2State.Game.State != GameStates.Ingame || pd2State.Players.LocalPlayer.FlashAmount <= 0) return EmptyLayer.Instance; var flashColor = ColorUtils.MultiplyColorByScalar(Properties.FlashbangColor, pd2State.Players.LocalPlayer.FlashAmount); - EffectLayer.FillOver(flashColor); + EffectLayer.FillOver(in flashColor); - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; } } \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/Profiles/Payday 2/Layers/PD2StatesLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/Payday 2/Layers/PD2StatesLayerHandler.cs index 6b429eff5..367d557c3 100644 --- a/Project-Aurora/Project-Aurora/Profiles/Payday 2/Layers/PD2StatesLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/Payday 2/Layers/PD2StatesLayerHandler.cs @@ -72,9 +72,9 @@ public override void Default() } -public class PD2StatesLayerHandler : LayerHandler +public class PD2StatesLayerHandler() : LayerHandler("PD2StatesLayerHandler") { - private readonly EffectLayer _swansongLayer = new("Payday 2 - Swansong", true); + private readonly EffectLayer _swansongLayer = new BitmapEffectLayer("Payday 2 - Swansong", true); protected override UserControl CreateControl() { @@ -83,9 +83,9 @@ protected override UserControl CreateControl() public override EffectLayer Render(IGameState gameState) { - if (gameState is not GameState_PD2 pd2State) return EffectLayer.EmptyLayer; + if (gameState is not GameState_PD2 pd2State) return EmptyLayer.Instance; - if (pd2State.Game.State != GameStates.Ingame) return EffectLayer.EmptyLayer; + if (pd2State.Game.State != GameStates.Ingame) return EmptyLayer.Instance; switch (pd2State.LocalPlayer.State) { case PlayerState.Incapacitated or PlayerState.Bleed_out or PlayerState.Fatal: @@ -99,8 +99,8 @@ public override EffectLayer Render(IGameState gameState) var incapColor = Color.FromArgb(incapAlpha, Properties.DownedColor); - EffectLayer.FillOver(incapColor); - EffectLayer.Set(DeviceKeys.Peripheral, incapColor); + EffectLayer.FillOver(in incapColor); + EffectLayer.Set(DeviceKeys.Peripheral, in incapColor); break; } case PlayerState.Arrested: @@ -116,7 +116,8 @@ public override EffectLayer Render(IGameState gameState) _swansongLayer.Set(DeviceKeys.Peripheral, swansongColor); - return EffectLayer + _swansongLayer; + EffectLayer.Add(_swansongLayer); + return EffectLayer; } protected override void PropertiesChanged(object? sender, PropertyChangedEventArgs args) diff --git a/Project-Aurora/Project-Aurora/Profiles/RocketLeague/Layers/RocketLeagueGoalExplosionLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/RocketLeague/Layers/RocketLeagueGoalExplosionLayerHandler.cs index edc326fbf..e369a738b 100644 --- a/Project-Aurora/Project-Aurora/Profiles/RocketLeague/Layers/RocketLeagueGoalExplosionLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/RocketLeague/Layers/RocketLeagueGoalExplosionLayerHandler.cs @@ -48,7 +48,7 @@ public override void Default() } } -public class RocketLeagueGoalExplosionLayerHandler() : LayerHandler("Goal Explosion") +public class RocketLeagueGoalExplosionLayerHandler() : LayerHandler("Goal Explosion") { private int _previousOwnTeamGoals; private int _previousOpponentGoals; @@ -77,10 +77,10 @@ public override EffectLayer Render(IGameState gameState) var goalExplosionMix = new AnimationMix(); if (gameState is not GameStateRocketLeague state) - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; if (state.Game.Status == RLStatus.Undefined) - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; if (state.YourTeam.Goals == -1 || state.OpponentTeam.Goals == -1 || _previousOwnTeamGoals > state.YourTeam.Goals || _previousOpponentGoals > state.OpponentTeam.Goals) { diff --git a/Project-Aurora/Project-Aurora/Profiles/Witcher3/Layers/Witcher3BackgroundLayerHandler.cs b/Project-Aurora/Project-Aurora/Profiles/Witcher3/Layers/Witcher3BackgroundLayerHandler.cs index 37873174a..dd4a17ab6 100644 --- a/Project-Aurora/Project-Aurora/Profiles/Witcher3/Layers/Witcher3BackgroundLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Profiles/Witcher3/Layers/Witcher3BackgroundLayerHandler.cs @@ -87,7 +87,7 @@ protected override UserControl CreateControl() public override EffectLayer Render(IGameState gameState) { - if (gameState is not GameStateWitcher3 witcher3State) return EffectLayer.EmptyLayer; + if (gameState is not GameStateWitcher3 witcher3State) return EmptyLayer.Instance; var bgColor = witcher3State.Player.ActiveSign switch { diff --git a/Project-Aurora/Project-Aurora/Settings/Controls/Control_LayerControlPresenter.xaml b/Project-Aurora/Project-Aurora/Settings/Controls/Control_LayerControlPresenter.xaml index 65b7fa86e..c95861bbc 100644 --- a/Project-Aurora/Project-Aurora/Settings/Controls/Control_LayerControlPresenter.xaml +++ b/Project-Aurora/Project-Aurora/Settings/Controls/Control_LayerControlPresenter.xaml @@ -7,44 +7,39 @@ xmlns:overrides="clr-namespace:AuroraRgb.Settings.Overrides" x:Class="AuroraRgb.Settings.Controls.Control_LayerControlPresenter" mc:Ignorable="d" d:DesignWidth="602" d:DesignHeight="201.525"> - - - - - - - - - - - - - diff --git a/Project-Aurora/Project-Aurora/Settings/Controls/Control_LayerControlPresenter.xaml.cs b/Project-Aurora/Project-Aurora/Settings/Controls/Control_LayerControlPresenter.xaml.cs index 012d92760..863d368e0 100644 --- a/Project-Aurora/Project-Aurora/Settings/Controls/Control_LayerControlPresenter.xaml.cs +++ b/Project-Aurora/Project-Aurora/Settings/Controls/Control_LayerControlPresenter.xaml.cs @@ -2,7 +2,9 @@ using System.Linq; using System.Windows; using System.Windows.Controls; +using System.Windows.Media; using System.Windows.Media.Effects; +using AuroraRgb.EffectsEngine; using AuroraRgb.Settings.Layers; namespace AuroraRgb.Settings.Controls; @@ -31,25 +33,45 @@ private async void SetLayer(Layer layer) DataContext = layer; - cmbLayerType.ItemsSource = layer.AssociatedApplication.AllowedLayers.OrderBy(l => l.Order).ThenBy(l => l.Name); - cmbLayerType.SelectedValue = Layer.Handler.GetType(); - - ctrlLayerTypeConfig.Content = EmptyContent; - ctrlLayerTypeConfig.Content = await layer.Control; - chk_ExcludeMask.IsChecked = Layer.Handler._EnableExclusionMask ?? false; - keyseq_ExcludeMask.Sequence = Layer.Handler._ExclusionMask; - sldr_Opacity.Value = (Layer.Handler._Opacity ?? 1d) * 100.0; - lbl_Opacity_Text.Text = $"{(int)sldr_Opacity.Value} %"; - - grdLayerConfigs.Visibility = Visibility.Hidden; - overridesEditor.Visibility = Visibility.Hidden; - btnConfig.Visibility = Visibility.Visible; - btnOverrides.Visibility = Visibility.Visible; - grd_LayerControl.IsHitTestVisible = true; - grd_LayerControl.Effect = null; + CmbLayerType.ItemsSource = layer.AssociatedApplication.AllowedLayers.OrderBy(l => l.Order).ThenBy(l => l.Name); + CmbLayerType.SelectedValue = Layer.Handler.GetType(); + + CtrlLayerTypeConfig.Content = EmptyContent; + CtrlLayerTypeConfig.Content = await layer.Control; + ChkExcludeMask.IsChecked = Layer.Handler._EnableExclusionMask ?? false; + KeyseqExcludeMask.Sequence = Layer.Handler._ExclusionMask; + SldrOpacity.Value = (Layer.Handler._Opacity ?? 1d) * 100.0; + LblOpacityText.Text = $"{(int)SldrOpacity.Value} %"; + + GrdLayerConfigs.Visibility = Visibility.Hidden; + OverridesEditor.Visibility = Visibility.Hidden; + BtnConfig.Visibility = Visibility.Visible; + BtnOverrides.Visibility = Visibility.Visible; + GrdLayerControl.IsHitTestVisible = true; + GrdLayerControl.Effect = null; _isSettingNewLayer = false; - overridesEditor.Layer = layer; + OverridesEditor.Layer = layer; + + var effectLayerType = layer.Handler.GetEffectLayerType(); + if (effectLayerType == typeof(NoRenderLayer)) + { + PowerTooltip.Visibility = Visibility.Visible; + PowerTooltip.Text = "\ud83c\udf43"; + PowerTooltip.CircleBackground = Brushes.Green; + PowerTooltip.HintTooltip = "Non-rendering layer. Minimum background usage"; + }else if (effectLayerType == typeof(BitmapEffectLayer)) + { + PowerTooltip.Visibility = Visibility.Visible; + PowerTooltip.Text = "\ud83c\udf42"; + PowerTooltip.CircleBackground = Brushes.Chocolate; + PowerTooltip.HintTooltip = "Rendering layer. Background usage may be high depending how often the visual changes"; + } + else + { + PowerTooltip.Visibility = Visibility.Hidden; + } + HighUsageTooltip.Visibility = layer.Handler.HighResource() ? Visibility.Visible : Visibility.Hidden; } private void cmbLayerType_SelectionChanged(object? sender, SelectionChangedEventArgs e) @@ -65,22 +87,22 @@ private async void ResetLayer(Type type) _Layer.Handler = (ILayerHandler)Activator.CreateInstance(type); - ctrlLayerTypeConfig.Content = EmptyContent; - ctrlLayerTypeConfig.Content = await _Layer.Control; - chk_ExcludeMask.IsChecked = Layer.Handler._EnableExclusionMask ?? false; - keyseq_ExcludeMask.Sequence = Layer.Handler._ExclusionMask; - sldr_Opacity.Value = (int)(Layer.Handler.Opacity * 100.0f); - lbl_Opacity_Text.Text = $"{(int)sldr_Opacity.Value} %"; + CtrlLayerTypeConfig.Content = EmptyContent; + CtrlLayerTypeConfig.Content = await _Layer.Control; + ChkExcludeMask.IsChecked = Layer.Handler._EnableExclusionMask ?? false; + KeyseqExcludeMask.Sequence = Layer.Handler._ExclusionMask; + SldrOpacity.Value = (int)(Layer.Handler.Opacity * 100.0f); + LblOpacityText.Text = $"{(int)SldrOpacity.Value} %"; _Layer.AssociatedApplication.SaveProfiles(); - overridesEditor.ForcePropertyListUpdate(); + OverridesEditor.ForcePropertyListUpdate(); } private void btnReset_Click(object? sender, RoutedEventArgs e) { if (IsLoaded && !_isSettingNewLayer && sender is Button) { - ResetLayer((Type)cmbLayerType.SelectedValue); + ResetLayer((Type)CmbLayerType.SelectedValue); } } @@ -88,11 +110,11 @@ private void btnConfig_Click(object? sender, RoutedEventArgs e) { if (!IsLoaded || _isSettingNewLayer || sender is not Button) return; - var v = grdLayerConfigs.IsVisible; - grdLayerConfigs.Visibility = v ? Visibility.Hidden : Visibility.Visible; - grd_LayerControl.IsHitTestVisible = v; - grd_LayerControl.Effect = v ? null : new BlurEffect(); - btnOverrides.Visibility = v ? Visibility.Visible : Visibility.Collapsed; + var v = GrdLayerConfigs.IsVisible; + GrdLayerConfigs.Visibility = v ? Visibility.Hidden : Visibility.Visible; + GrdLayerControl.IsHitTestVisible = v; + GrdLayerControl.Effect = v ? null : new BlurEffect(); + BtnOverrides.Visibility = v ? Visibility.Visible : Visibility.Collapsed; } private void chk_ExcludeMask_Checked(object? sender, RoutedEventArgs e) @@ -112,16 +134,16 @@ private void sldr_Opacity_ValueChanged(object? sender, RoutedPropertyChangedEven if (!IsLoaded || _isSettingNewLayer) return; Layer.Handler._Opacity = (float)e.NewValue / 100.0f; - lbl_Opacity_Text.Text = $"{(int)e.NewValue} %"; + LblOpacityText.Text = $"{(int)e.NewValue} %"; } private void btnOverrides_Click(object? sender, RoutedEventArgs e) { if (!IsLoaded || _isSettingNewLayer) return; - var v = overridesEditor.IsVisible; - overridesEditor.Visibility = v ? Visibility.Hidden : Visibility.Visible; - grd_LayerControl.IsHitTestVisible = v; - btnConfig.Visibility = v ? Visibility.Visible : Visibility.Collapsed; + var v = OverridesEditor.IsVisible; + OverridesEditor.Visibility = v ? Visibility.Hidden : Visibility.Visible; + GrdLayerControl.IsHitTestVisible = v; + BtnConfig.Visibility = v ? Visibility.Visible : Visibility.Collapsed; } } \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/AmbilightLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/AmbilightLayerHandler.cs index c75639ac4..a20ab418e 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/AmbilightLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/AmbilightLayerHandler.cs @@ -211,7 +211,7 @@ public override void Default() [LogicOverrideIgnoreProperty("SecondaryColor")] [LogicOverrideIgnoreProperty("_Sequence")] [DoNotNotify] -public sealed class AmbilightLayerHandler : LayerHandler +public sealed class AmbilightLayerHandler : LayerHandler { private readonly Temporary _screenCapture; @@ -258,7 +258,7 @@ public AmbilightLayerHandler() : base("Ambilight Layer") public override EffectLayer Render(IGameState gameState) { if (Properties.Sequence.GetAffectedRegion().IsEmpty) - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; if (_captureWorker.WaitingCallbacks < 1) { @@ -326,6 +326,11 @@ protected override UserControl CreateControl() return new Control_AmbilightLayer(this); } + public override bool HighResource() + { + return true; + } + private object? TakeScreenshot(object? sender) { try diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/AnimationLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/AnimationLayerHandler.cs index 8550a92b3..e5916bd83 100755 --- a/Project-Aurora/Project-Aurora/Settings/Layers/AnimationLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/AnimationLayerHandler.cs @@ -100,7 +100,7 @@ public override void Default() { [LogicOverrideIgnoreProperty("PrimaryColor")] [LogicOverrideIgnoreProperty("SecondaryColor")] - public sealed class AnimationLayerHandler() : LayerHandler("Animation Layer") + public sealed class AnimationLayerHandler() : LayerHandler("Animation Layer") { private readonly List _runningAnimations = []; private readonly Stopwatch _animTimeStopwatch = new(); @@ -122,6 +122,11 @@ protected override UserControl CreateControl() { return new Control_AnimationLayer(this); } + public override bool HighResource() + { + return true; + } + public override void Dispose() { InputsModule.InputEvents.Result.KeyDown -= InputEvents_KeyDown; diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/DefaultLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/DefaultLayerHandler.cs index 2132577f2..e2945aa8a 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/DefaultLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/DefaultLayerHandler.cs @@ -1,20 +1,20 @@ using System.Windows.Controls; +using AuroraRgb.EffectsEngine; using AuroraRgb.Profiles; using AuroraRgb.Settings.Layers.Controls; using AuroraRgb.Settings.Overrides; -namespace AuroraRgb.Settings.Layers +namespace AuroraRgb.Settings.Layers; + +[LogicOverrideIgnoreProperty("_PrimaryColor")] +[LogicOverrideIgnoreProperty("_Opacity")] +[LogicOverrideIgnoreProperty("_Enabled")] +[LogicOverrideIgnoreProperty("_Sequence")] +[LayerHandlerMeta(Order = -1, IsDefault = true)] +public class DefaultLayerHandler : LayerHandler { - [LogicOverrideIgnoreProperty("_PrimaryColor")] - [LogicOverrideIgnoreProperty("_Opacity")] - [LogicOverrideIgnoreProperty("_Enabled")] - [LogicOverrideIgnoreProperty("_Sequence")] - [LayerHandlerMeta(Order = -1, IsDefault = true)] - public class DefaultLayerHandler : LayerHandler + protected override UserControl CreateControl() { - protected override UserControl CreateControl() - { - return new Control_DefaultLayer(); - } + return new Control_DefaultLayer(); } -} +} \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/EqualizerLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/EqualizerLayerHandler.cs index fedc79a80..3085eaa4e 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/EqualizerLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/EqualizerLayerHandler.cs @@ -17,6 +17,7 @@ using NAudio.Dsp; using NAudio.Wave; using Newtonsoft.Json; +using Complex = NAudio.Dsp.Complex; namespace AuroraRgb.Settings.Layers; @@ -192,7 +193,7 @@ public override void Default() } [LayerHandlerMeta(Name = "Audio Visualizer", IsDefault = true)] -public sealed class EqualizerLayerHandler : LayerHandler +public sealed class EqualizerLayerHandler : LayerHandler { public event NewLayerRendered? NewLayerRender = delegate { }; @@ -238,7 +239,6 @@ public EqualizerLayerHandler(): base("EqualizerLayer") _ffts = new Complex[FftLength]; _sampleAggregator.FftCalculated += FftCalculated; - _sampleAggregator.PerformFft = true; _deviceProxy = new Temporary(() => { @@ -265,7 +265,7 @@ protected override UserControl CreateControl() } public override EffectLayer Render(IGameState gamestate) { - if (_disposed) return EffectLayer.EmptyLayer; + if (_disposed) return EmptyLayer.Instance; var deviceProxy = _deviceProxy.Value; var deviceProxyFlow = Properties.DeviceFlow switch @@ -277,7 +277,7 @@ public override EffectLayer Render(IGameState gamestate) deviceProxy.DeviceId = Properties.DeviceId; if (deviceProxy.Device == null) - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; // The system sound as a value between 0.0 and 1.0 var systemSoundNormalized = deviceProxy.Device?.AudioMeterInformation?.MasterPeakValue ?? 1f; @@ -295,6 +295,14 @@ public override EffectLayer Render(IGameState gamestate) var localFft = _ffts; + // do transformations before using them + for (var position = 0; position < localFft.Length; position++) + { + var complex = localFft[position]; + var val = complex.X; + complex.X = val * (float)FastFourierTransform.HannWindow(position, localFft.Length); + } + var bgEnabled = false; switch (Properties.BackgroundMode) { @@ -418,17 +426,20 @@ private void OnDataAvailable(object? sender, WaveInEventArgs e) var fftIndexRatio = (double)FftLength / bufferCount; var buffer = waveBuffer.FloatBuffer; - for (var freq = 0; freq < bufferCount; freq += _channels) + for (var freqPlusChannel = 0; freqPlusChannel < bufferCount; freqPlusChannel += _channels) { - var fftIndex = (int)Math.Floor(freq * fftIndexRatio); - - var max = 0d; - for (var c = 0; c < _channels; c++) + var max = 0f; + var nextFreq = freqPlusChannel + _channels; + + for (var i = freqPlusChannel; i < nextFreq; i++) { - max = Math.Max(max, buffer[freq + c]); + max = Math.Max(max, buffer[i]); } + + var fftIndex = (int)Math.Floor(freqPlusChannel * fftIndexRatio); _sampleAggregator.Add(max, fftIndex); } + waveBuffer.Clear(); _sampleAggregator.Complete(); } @@ -513,23 +524,23 @@ public override void Dispose() public class SampleAggregator { + private readonly int _fftLength; + // FFT public event EventHandler? FftCalculated; - public bool PerformFft { get; set; } // This Complex is NAudio's own! private readonly Complex[] _fftBuffer; private readonly FftEventArgs _fftArgs; - private readonly int _fftLength; public SampleAggregator(int fftLength) { + _fftLength = fftLength; if (!IsPowerOfTwo(fftLength)) { throw new ArgumentException("FFT Length must be a power of two"); } - _fftLength = fftLength; _fftBuffer = new Complex[fftLength]; _fftArgs = new FftEventArgs(_fftBuffer); } @@ -539,16 +550,20 @@ private bool IsPowerOfTwo(int x) return (x & (x - 1)) == 0; } - public void Add(double value, int position) + public void Add(float value, int position) { - if (!PerformFft || FftCalculated == null) return; + if (FftCalculated == null) return; // Remember the window function! There are many others as well. - if (float.IsNaN(_fftBuffer[position].X)) + var p = _fftBuffer[position]; + if (float.IsNaN(p.X)) { - _fftBuffer[position].X = 0; + p.X = 0; } - _fftBuffer[position].X = Math.Max(_fftBuffer[position].X, (float)(value * FastFourierTransform.HannWindow(position, _fftLength))); - _fftBuffer[position].Y = 0; // This is always zero with audio. + + // just save the max value. Transformations will be done when they are needed + p.X = Math.Max(p.X, value); + p.Y = 0; // This is always zero with audio. + _fftBuffer[position] = p; } public void Complete() diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/GlitchLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/GlitchLayerHandler.cs index 78292224d..afc6f148c 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/GlitchLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/GlitchLayerHandler.cs @@ -91,7 +91,6 @@ protected override void PropertiesChanged(object? sender, PropertyChangedEventAr { base.PropertiesChanged(sender, args); _glitchColors.Clear(); - EffectLayer.Invalidate(); } } diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/GradientLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/GradientLayerHandler.cs index c7de3e87b..73a3c6ff1 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/GradientLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/GradientLayerHandler.cs @@ -37,9 +37,9 @@ private static LayerEffectConfig DefaultGradientConfig() [LogicOverrideIgnoreProperty("_PrimaryColor")] [LogicOverrideIgnoreProperty("SecondaryColor")] -public class GradientLayerHandler : LayerHandler +public class GradientLayerHandler : LayerHandler { - private readonly EffectLayer _tempLayerBitmap = new("GradientLayer - Colors", true); + private readonly BitmapEffectLayer _tempLayerWrapped = new("GradientLayer - Colors", true); private readonly Action _gradientRenderFunc; public GradientLayerHandler(): base("GradientLayer") @@ -47,7 +47,7 @@ public GradientLayerHandler(): base("GradientLayer") Properties.PropertyChanged += PropertiesChanged; _gradientRenderFunc = g => { - g.DrawRectangle(_tempLayerBitmap, _tempLayerBitmap.Dimension); + g.DrawRectangle(_tempLayerWrapped); }; } @@ -75,7 +75,7 @@ public override EffectLayer Render(IGameState gameState) } else { - _tempLayerBitmap.DrawGradient(LayerEffects.GradientShift_Custom_Angle, Properties.GradientConfig); + _tempLayerWrapped.DrawGradient(LayerEffects.GradientShift_Custom_Angle, Properties.GradientConfig); EffectLayer.Clear(); EffectLayer.DrawTransformed( Properties.Sequence, diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/ILayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/ILayerHandler.cs index 6ce542bb9..dd0de60e1 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/ILayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/ILayerHandler.cs @@ -27,4 +27,7 @@ public interface ILayerHandler: IDisposable void SetApplication(Application profile); void SetGameState(IGameState gamestate); + + Type GetEffectLayerType(); + bool HighResource(); } \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/ImageLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/ImageLayerHandler.cs index 79d45e0a0..bddf04e11 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/ImageLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/ImageLayerHandler.cs @@ -30,7 +30,7 @@ public override void Default() [LogicOverrideIgnoreProperty("_PrimaryColor")] [LogicOverrideIgnoreProperty("SecondaryColor")] -public class ImageLayerHandler() : LayerHandler("ImageLayer") +public class ImageLayerHandler() : LayerHandler("ImageLayer") { private Image? _loadedImage; private string? _loadedImagePath = ""; @@ -42,13 +42,13 @@ protected override UserControl CreateControl() public override EffectLayer Render(IGameState gamestate) { - if (string.IsNullOrWhiteSpace(Properties.ImagePath)) return EffectLayer.EmptyLayer; + if (string.IsNullOrWhiteSpace(Properties.ImagePath)) return EmptyLayer.Instance; if (_loadedImagePath != Properties.ImagePath) { //Not loaded, load it! if (!File.Exists(Properties.ImagePath)) - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; _loadedImage?.Dispose(); _loadedImage = new Bitmap(Properties.ImagePath); diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/InteractiveLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/InteractiveLayerHandler.cs index a1da8a5ac..f4cd58fb2 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/InteractiveLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/InteractiveLayerHandler.cs @@ -137,7 +137,7 @@ public override void Default() } } -public sealed class InteractiveLayerHandler : LayerHandler +public sealed class InteractiveLayerHandler : LayerHandler { private readonly Func, bool> _keysToRemove; private readonly ConcurrentDictionary _inputDictionary = new(new Dictionary(Effects.MaxDeviceId)); @@ -349,7 +349,7 @@ public override EffectLayer Render(IGameState gamestate) if (_inputDictionary.Values.Count == 0) { - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; } EffectLayer.Clear(); diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/Layer.cs b/Project-Aurora/Project-Aurora/Settings/Layers/Layer.cs index c018b794a..d920b9985 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/Layer.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/Layer.cs @@ -93,7 +93,7 @@ public EffectLayer Render(IGameState gs) } if (!Handler.Properties.Enabled) - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; try { var effectLayer = Handler.PostRenderFX(Handler.Render(gs)); @@ -110,12 +110,12 @@ public EffectLayer Render(IGameState gs) var controlInterface = appAuroraApp.ControlInterface; controlInterface.ShowErrorNotification($"Layer \'{Name}\" fails to render. Check logs for details"); - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; } Global.logger.Error(e, "Layer render error"); } - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; } public void SetProfile(Application profile) { diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/LayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/LayerHandler.cs index 3e99273b8..aff21fc21 100755 --- a/Project-Aurora/Project-Aurora/Settings/Layers/LayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/LayerHandler.cs @@ -16,10 +16,12 @@ namespace AuroraRgb.Settings.Layers; [UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)] -public abstract class LayerHandler : ILayerHandler where TProperty : LayerHandlerProperties +public abstract class LayerHandler : ILayerHandler + where TProperty : LayerHandlerProperties + where LayerType : EffectLayer { private readonly Temporary> _control; - + [JsonIgnore] public Application Application { get; protected set; } @@ -70,7 +72,7 @@ public double? _Opacity { private readonly Temporary _effectLayer; private static readonly PropertyChangedEventArgs ConstPropertyChangedEventArgs = new(""); - protected EffectLayer EffectLayer + protected LayerType EffectLayer { get { @@ -79,7 +81,7 @@ protected EffectLayer EffectLayer var _ = _effectLayer.Value; PropertiesChanged(this, ConstPropertyChangedEventArgs); } - return _effectLayer.Value; + return (LayerType)_effectLayer.Value; } } @@ -89,7 +91,7 @@ protected LayerHandler(): this("Unoptimized Layer"){} protected LayerHandler(string name) { - _effectLayer = new(() => new EffectLayer(name, true)); + _effectLayer = new(() => CreateLayer(name)); _ExclusionMask = new KeySequence(); Properties.PropertyChanged += PropertiesChanged; WeakEventManager.AddHandler(null, nameof(Effects.CanvasChanged), PropertiesChanged); @@ -97,9 +99,23 @@ protected LayerHandler(string name) _control = new Temporary>(CreateControlOnMain, false); } + private EffectLayer CreateLayer(string name) + { + if (typeof(LayerType) == typeof(BitmapEffectLayer)) + { + return new BitmapEffectLayer(name, true); + } + if (typeof(LayerType) == typeof(NoRenderLayer)) + { + return new NoRenderLayer(); + } + + throw new NotImplementedException(); + } + public virtual EffectLayer Render(IGameState gameState) { - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; } public virtual void SetGameState(IGameState gamestate) @@ -107,13 +123,22 @@ public virtual void SetGameState(IGameState gamestate) } - public EffectLayer PostRenderFX(EffectLayer renderedLayer) + public Type GetEffectLayerType() { + return typeof(LayerType); + } + + public virtual bool HighResource() + { + return false; + } + public EffectLayer PostRenderFX(EffectLayer renderedLayer) + { //Last PostFX is exclusion renderedLayer.Exclude(EnableExclusionMask ? ExclusionMask : KeySequence.Empty); - renderedLayer *= Properties.LayerOpacity; + renderedLayer.SetOpacity(Properties.LayerOpacity); return renderedLayer; } @@ -187,4 +212,8 @@ public virtual void Dispose() } [LayerHandlerMeta(Exclude = true)] -public abstract class LayerHandler(string name) : LayerHandler(name); \ No newline at end of file +public abstract class LayerHandler(string name) : LayerHandler(name); + +[LayerHandlerMeta(Exclude = true)] +public abstract class LayerHandler(string name) : LayerHandler(name) + where Props : LayerHandlerProperties; \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/LogitechLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/LogitechLayerHandler.cs index cc7ea90c9..a7a121995 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/LogitechLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/LogitechLayerHandler.cs @@ -71,8 +71,6 @@ public override void Default() [LayerHandlerMeta(Name = "Logitech Lightsync", IsDefault = true)] public sealed class LogitechLayerHandler : LayerHandler { - private readonly SingleColorBrush _background = new(SimpleColor.Transparent); - public LogitechLayerHandler() : base("Logitech Layer") { LogitechSdkModule.LogitechSdkListener.ColorsUpdated += LogitechSdkListenerOnColorsUpdated; @@ -88,12 +86,12 @@ private void LogitechSdkListenerOnColorsUpdated(object? sender, EventArgs e) Invalidated = true; } - public override EffectLayer Render(IGameState gamestate) + public override EffectLayer Render(IGameState gameState) { var logitechSdk = LogitechSdkModule.LogitechSdkListener; if (logitechSdk.State != LightsyncSdkState.Connected) { - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; } if (!Invalidated) @@ -101,8 +99,7 @@ public override EffectLayer Render(IGameState gamestate) return EffectLayer; } - _background.Color = logitechSdk.BackgroundColor; - EffectLayer.Fill(_background); + EffectLayer.Fill((Color)logitechSdk.BackgroundColor); foreach (var kv in logitechSdk.Colors) { var color = Properties.ColorPostProcessEnabled ? PostProcessColor(kv.Value) : kv.Value; diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/ParticleLayerHandlerBase.cs b/Project-Aurora/Project-Aurora/Settings/Layers/ParticleLayerHandlerBase.cs index 516d9bbc4..badd77ccc 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/ParticleLayerHandlerBase.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/ParticleLayerHandlerBase.cs @@ -40,7 +40,7 @@ public override void Default() { /// /// /// - public abstract class ParticleLayerHandlerBase : LayerHandler, INotifyRender + public abstract class ParticleLayerHandlerBase : LayerHandler, INotifyRender where TParticle : IParticle where TProperties : ParticleLayerPropertiesBase { diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/PercentGradientLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/PercentGradientLayerHandler.cs index 2dea9628e..1cdc9ee63 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/PercentGradientLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/PercentGradientLayerHandler.cs @@ -31,7 +31,6 @@ public override void Default() [LayerHandlerMeta(Name = "Percent (Gradient)", IsDefault = true)] public class PercentGradientLayerHandler : PercentLayerHandler { - protected override UserControl CreateControl() { return new Control_PercentGradientLayer(this); diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/PercentLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/PercentLayerHandler.cs index a4a43814d..c6331acf7 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/PercentLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/PercentLayerHandler.cs @@ -1,4 +1,6 @@ -using System.Globalization; +using System.ComponentModel; +using System.Globalization; +using System.Linq; using System.Windows.Controls; using AuroraRgb.EffectsEngine; using AuroraRgb.Profiles; @@ -84,29 +86,45 @@ public override void Default() } } -public class PercentLayerHandler() : LayerHandler("PercentLayer") +public class PercentLayerHandler() : LayerHandler("PercentLayer") where TProperty : PercentLayerHandlerProperties { private double _value; - public override EffectLayer Render(IGameState state) + private readonly NoRenderLayer NoRenderLayer = new(); + + public override EffectLayer Render(IGameState gameState) { + var keySequence = Properties.Sequence; + EffectLayer layer = keySequence.Type switch + { + KeySequenceType.Sequence => NoRenderLayer, + _ => EffectLayer, + }; + + if (Invalidated) { - EffectLayer.Clear(); + layer.Clear(); Invalidated = false; _value = -1; } - var value = Properties.Logic?._Value ?? state.GetNumber(Properties.VariablePath); + var value = Properties.Logic?._Value ?? gameState.GetNumber(Properties.VariablePath); if (MathUtils.NearlyEqual(_value, value, 0.000001)) { - return EffectLayer; + return layer; } _value = value; - var maxvalue = Properties.Logic?._MaxValue ?? state.GetNumber(Properties.MaxVariablePath); + var maxvalue = Properties.Logic?._MaxValue ?? gameState.GetNumber(Properties.MaxVariablePath); - EffectLayer.PercentEffect(Properties.PrimaryColor, Properties.SecondaryColor, Properties.Sequence, value, maxvalue, + if (keySequence.Type == KeySequenceType.Sequence) + { + NoRenderLayer.PercentEffect(Properties.PrimaryColor, Properties.SecondaryColor, keySequence.Keys, value, maxvalue, + Properties.PercentType, Properties.BlinkThreshold, Properties.BlinkDirection, Properties.BlinkBackground); + return NoRenderLayer; + } + EffectLayer.PercentEffect(Properties.PrimaryColor, Properties.SecondaryColor, keySequence, value, maxvalue, Properties.PercentType, Properties.BlinkThreshold, Properties.BlinkDirection, Properties.BlinkBackground); return EffectLayer; } diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/RadialLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/RadialLayerHandler.cs index f0e5b8956..e7fd5fffb 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/RadialLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/RadialLayerHandler.cs @@ -37,7 +37,7 @@ public override void Default() { } } -public class RadialLayerHandler() : LayerHandler("RadialLayer") +public class RadialLayerHandler() : LayerHandler("RadialLayer") { private readonly Stopwatch _sw = new(); private float _angle; diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/RazerLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/RazerLayerHandler.cs index 8dc0ae6f4..e16b04b44 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/RazerLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/RazerLayerHandler.cs @@ -95,7 +95,7 @@ public override EffectLayer Render(IGameState gamestate) { if (!RzHelper.IsCurrentAppValid()) { - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; } if (RzHelper.IsStale()) return EffectLayer; diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/ScriptLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/ScriptLayerHandler.cs index 9ba999171..3da8ce798 100755 --- a/Project-Aurora/Project-Aurora/Settings/Layers/ScriptLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/ScriptLayerHandler.cs @@ -46,7 +46,7 @@ public override void Default() [LogicOverrideIgnoreProperty("_PrimaryColor")] [LogicOverrideIgnoreProperty("_Sequence")] -public class ScriptLayerHandler : LayerHandler, INotifyPropertyChanged +public class ScriptLayerHandler : LayerHandler, INotifyPropertyChanged { public event PropertyChangedEventHandler? PropertyChanged; @@ -56,7 +56,7 @@ public class ScriptLayerHandler : LayerHandler, IN public override EffectLayer Render(IGameState gamestate) { - if (!IsScriptValid) return EffectLayer.EmptyLayer; + if (!IsScriptValid) return EmptyLayer.Instance; try { var script = Application.EffectScripts[Properties.Script]; diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/ShortcutAssistantLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/ShortcutAssistantLayerHandler.cs index 569515cdf..650c712c9 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/ShortcutAssistantLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/ShortcutAssistantLayerHandler.cs @@ -216,7 +216,7 @@ public override EffectLayer Render(IGameState gamestate) { if (!IsLayerActive(out var heldKeys)) { - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; } // The layer is active. At this point we have at least 1 key to highlight @@ -225,7 +225,7 @@ public override EffectLayer Render(IGameState gamestate) var currentShortcutNode = Properties.ShortcutKeysTree.GetNodeByPath(heldKeysToHighlight); if (currentShortcutNode == null) { - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; } if (Properties.LeafShortcutAlwaysOn.GetValueOrDefault(false) && currentShortcutNode.IsLeaf) { @@ -233,7 +233,7 @@ public override EffectLayer Render(IGameState gamestate) currentShortcutNode = Properties.ShortcutKeysTree.GetNodeByPath(heldKeysToHighlight.Take(heldKeysToHighlight.Length - 1).ToArray()); if (currentShortcutNode == null) { - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; } } diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/SolidColorLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/SolidColorLayerHandler.cs index 1639eaa3f..a902f8beb 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/SolidColorLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/SolidColorLayerHandler.cs @@ -10,7 +10,6 @@ namespace AuroraRgb.Settings.Layers; public class SolidColorLayerHandler() : LayerHandler("SolidColorLayer") { - private readonly SingleColorBrush _brush = new(); private KeySequence _propertiesSequence = new(); protected override UserControl CreateControl() @@ -20,22 +19,13 @@ protected override UserControl CreateControl() public override EffectLayer Render(IGameState gameState) { - EffectLayer.Set(_propertiesSequence, _brush); + EffectLayer.Set(_propertiesSequence, Properties.PrimaryColor); return EffectLayer; } protected override void PropertiesChanged(object? sender, PropertyChangedEventArgs args) { base.PropertiesChanged(sender, args); - _brush.Color = (SimpleColor)Properties.PrimaryColor; _propertiesSequence = Properties.Sequence; - EffectLayer.Invalidate(); - } - - public override void Dispose() - { - base.Dispose(); - - _brush.Dispose(); } } \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/SolidFillLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/SolidFillLayerHandler.cs index 815152d75..f14201a4e 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/SolidFillLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/SolidFillLayerHandler.cs @@ -20,10 +20,8 @@ public override void Default() } [LogicOverrideIgnoreProperty("_Sequence")] -public sealed class SolidFillLayerHandler : LayerHandler +public sealed class SolidFillLayerHandler() : LayerHandler("SolidFillLayerHandler") { - private readonly SingleColorBrush _solidBrush = new(SimpleColor.Transparent); - protected override UserControl CreateControl() { return new Control_SolidFillLayer(this); @@ -31,8 +29,7 @@ protected override UserControl CreateControl() public override EffectLayer Render(IGameState gameState) { - _solidBrush.Color = (SimpleColor)Properties.PrimaryColor; - EffectLayer.Set(Effects.Canvas.EntireSequence, _solidBrush); + EffectLayer.Fill(Properties.PrimaryColor); return EffectLayer; } } \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/TimerLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/TimerLayerHandler.cs index 771826f61..64fea3c77 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/TimerLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/TimerLayerHandler.cs @@ -60,7 +60,7 @@ public override void Default() { } } -public class TimerLayerHandler : LayerHandler { +public class TimerLayerHandler : LayerHandler { private readonly CustomTimer _timer; private bool _isActive; diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/ToggleKeyLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/ToggleKeyLayerHandler.cs index a908a0de0..414dff60c 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/ToggleKeyLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/ToggleKeyLayerHandler.cs @@ -28,17 +28,9 @@ public override void Default() { } } -public sealed class ToggleKeyLayerHandler : LayerHandler +public sealed class ToggleKeyLayerHandler() : LayerHandler("ToggleKeyLayer") { private bool _state = true; - private readonly SingleColorBrush _primaryBrush; - private readonly SingleColorBrush _secondaryBrush; - - public ToggleKeyLayerHandler(): base("ToggleKeyLayer") - { - _primaryBrush = new SingleColorBrush((SimpleColor)Properties.PrimaryColor); - _secondaryBrush = new SingleColorBrush((SimpleColor)Properties.SecondaryColor); - } protected override async Task Initialize() { @@ -50,8 +42,6 @@ protected override async Task Initialize() public override void Dispose() { InputsModule.InputEvents.Result.KeyDown -= InputEvents_KeyDown; - _primaryBrush.Dispose(); - _secondaryBrush.Dispose(); base.Dispose(); } @@ -67,18 +57,10 @@ public override EffectLayer Render(IGameState gameState) EffectLayer.Clear(); Invalidated = false; } - EffectLayer.Set(Properties.Sequence, _state ? _primaryBrush : _secondaryBrush); + EffectLayer.Set(Properties.Sequence, _state ? Properties.PrimaryColor : Properties.SecondaryColor); return EffectLayer; } - protected override void PropertiesChanged(object? sender, PropertyChangedEventArgs args) - { - base.PropertiesChanged(sender, args); - - _primaryBrush.Color = (SimpleColor)Properties.PrimaryColor; - _secondaryBrush.Color = (SimpleColor)Properties.SecondaryColor; - } - private void InputEvents_KeyDown(object? sender, EventArgs e) { foreach (var kb in Properties.TriggerKeys) diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/WrapperLightsLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/WrapperLightsLayerHandler.cs index 5791023ef..5aec00ccc 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/WrapperLightsLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/WrapperLightsLayerHandler.cs @@ -93,7 +93,6 @@ public class WrapperLightsLayerHandler() : LayerHandler _extraKeys = new(); private Color _lastFillColor = Color.Black; private EntireEffect? _currentEffect; - private readonly SingleColorBrush _fillBrush = new(SimpleColor.Transparent); protected override UserControl CreateControl() { @@ -103,10 +102,9 @@ protected override UserControl CreateControl() public override EffectLayer Render(IGameState gamestate) { if (gamestate is not GameState_Wrapper) - return EffectLayer.EmptyLayer; + return EmptyLayer.Instance; - _fillBrush.Color = (SimpleColor)GetBoostedColor(_lastFillColor); - EffectLayer.Fill(_fillBrush); + EffectLayer.Fill(GetBoostedColor(_lastFillColor)); var allKeys = Enum.GetValues(typeof(DeviceKeys)).Cast().ToArray(); foreach (var key in allKeys) diff --git a/Project-Aurora/Project-Aurora/Utils/ColorUtils.cs b/Project-Aurora/Project-Aurora/Utils/ColorUtils.cs index 60a49e650..8b748574a 100644 --- a/Project-Aurora/Project-Aurora/Utils/ColorUtils.cs +++ b/Project-Aurora/Project-Aurora/Utils/ColorUtils.cs @@ -37,8 +37,6 @@ public static DrawingColor Clone(this DrawingColor clr) /// public static class ColorUtils { - private static readonly Random Randomizer = new(); - /// /// Converts from System.Windows.Media.Color to System.Drawing.Color /// @@ -98,7 +96,7 @@ public static DrawingColor GetAverageColor(BitmapSource bitmap) return CommonColorUtils.FastColor((byte)(red / numPixels), (byte)(green / numPixels), (byte)(blue / numPixels)); } - public static DrawingColor BlendColors(DrawingColor background, DrawingColor foreground, double percent) + public static DrawingColor BlendColors(in DrawingColor background, DrawingColor foreground, double percent) { return CommonColorUtils.BlendColors(background, foreground, percent); } diff --git a/Project-Aurora/Project-Aurora/Vorons/PerfEffect.cs b/Project-Aurora/Project-Aurora/Vorons/PerfEffect.cs index f953c7bd5..e51bbe12c 100644 --- a/Project-Aurora/Project-Aurora/Vorons/PerfEffect.cs +++ b/Project-Aurora/Project-Aurora/Vorons/PerfEffect.cs @@ -119,7 +119,7 @@ public PerformanceEffect() Properties.RegProp("Cycled Gradient Shift Full Speed", 100L, "Cycled gradient shifting speed at 100%", -1000L, 1000L); - _effectLayer = new EffectLayer(ID, true); + _effectLayer = new BitmapEffectLayer(ID, true); } private static readonly MathParser MathParser = new(); @@ -132,7 +132,7 @@ public PerformanceEffect() private static readonly ConcurrentDictionary CircleShidtStates = new(); - private readonly EffectLayer _effectLayer; + private readonly BitmapEffectLayer _effectLayer; private KeySequence Keys { get; set; } private EffectTypes EffectType { get; set; } diff --git a/Project-Aurora/Project-Aurora/Vorons/PingEffect.cs b/Project-Aurora/Project-Aurora/Vorons/PingEffect.cs index 10e343925..fdb226de8 100644 --- a/Project-Aurora/Project-Aurora/Vorons/PingEffect.cs +++ b/Project-Aurora/Project-Aurora/Vorons/PingEffect.cs @@ -95,13 +95,13 @@ public PingEffect() Properties.RegProp("Number of Pings in graph mode", 10L, "Amount of last pings that will be used to display graph.", 1, 50); //Properties.RegProp("BrightMode", false); - _layer = new EffectLayer(ID, true); + _layer = new BitmapEffectLayer(ID, true); } private static readonly ConcurrentDictionary, KeyValuePair> PingAnimations = new(); private static readonly ConcurrentDictionary Gradients = new(); - private readonly EffectLayer _layer; + private readonly BitmapEffectLayer _layer; private KeySequence Keys { get; set; } private EffectTypes EffectType { get; set; } @@ -184,7 +184,7 @@ public object UpdateLights(VariableRegistry properties, IGameState state = null) return _layer; } - public void Render(EffectLayer effectLayer) + public void Render(BitmapEffectLayer effectLayer) { // layers | | // 0.OldReplyPingBar |==========> | @@ -496,7 +496,7 @@ public void Add(ColorSpectrum spectrum, float gradientStart, float gradientEnd, } } - public void Draw(FreeFormObject freeform, EffectLayer effectLayer) + public void Draw(FreeFormObject freeform, BitmapEffectLayer effectLayer) { var g = effectLayer.GetGraphics(); var xPos = (float)Math.Round((freeform.X + Effects.Canvas.GridBaselineX) * Effects.Canvas.EditorToCanvasWidth);