using System; using System.Collections.Generic; using System.Linq; using Artemis.Storage.Entities.Profile; using SkiaSharp; namespace Artemis.Core.LayerBrushes; /// /// For internal use only, please use or or instead /// public abstract class BaseLayerBrush : BreakableModel, IDisposable, IPluginFeatureDependent { private LayerBrushType _brushType; private ILayerBrushConfigurationDialog? _configurationDialog; private LayerBrushDescriptor _descriptor; private Layer _layer; private bool _supportsTransformation = true; /// /// Creates a new instance of the class /// protected BaseLayerBrush() { // Both are set right after construction to keep the constructor of inherited classes clean _layer = null!; _descriptor = null!; LayerBrushEntity = null!; } /// /// Gets the layer this brush is applied to /// public Layer Layer { get => _layer; internal set => SetAndNotify(ref _layer, value); } /// /// Gets the brush entity this brush uses for persistent storage /// public LayerBrushEntity LayerBrushEntity { get; internal set; } /// /// Gets the descriptor of this brush /// public LayerBrushDescriptor Descriptor { get => _descriptor; internal set => SetAndNotify(ref _descriptor, value); } /// /// Gets or sets a configuration dialog complementing the regular properties /// public ILayerBrushConfigurationDialog? ConfigurationDialog { get => _configurationDialog; protected set => SetAndNotify(ref _configurationDialog, value); } /// /// Gets the type of layer brush /// public LayerBrushType BrushType { get => _brushType; internal set => SetAndNotify(ref _brushType, value); } /// /// Gets the ID of the that provided this effect /// public string? ProviderId => Descriptor?.Provider.Id; /// /// Gets a reference to the layer property group without knowing it's type /// public virtual LayerPropertyGroup? BaseProperties => null; /// /// Gets a list of presets available to this layer brush /// public virtual List? Presets => null; /// /// Gets the default preset used for new instances of this layer brush /// public virtual ILayerBrushPreset? DefaultPreset => Presets?.FirstOrDefault(); /// /// Gets a boolean indicating whether the layer brush is enabled or not /// public bool Enabled { get; private set; } /// /// Gets or sets whether the brush supports transformations /// Note: RGB.NET brushes can never be transformed and setting this to true will throw an exception /// public bool SupportsTransformation { get => _supportsTransformation; protected set { if (value && BrushType == LayerBrushType.RgbNet) throw new ArtemisPluginFeatureException(Descriptor?.Provider!, "An RGB.NET brush cannot support transformation"); _supportsTransformation = value; } } #region Overrides of BreakableModel /// public override string BrokenDisplayName => Descriptor.DisplayName; #endregion /// /// Called when the layer brush is activated /// public abstract void EnableLayerBrush(); /// /// Called when the layer brush is deactivated /// public abstract void DisableLayerBrush(); /// /// Called before rendering every frame, write your update logic here /// /// Seconds passed since last update public abstract void Update(double deltaTime); /// /// Releases the unmanaged resources used by the object and optionally releases the managed resources. /// /// /// to release both managed and unmanaged resources; /// to release only unmanaged resources. /// protected virtual void Dispose(bool disposing) { if (disposing) { DisableLayerBrush(); BaseProperties?.Dispose(); } } internal void InternalUpdate(Timeline timeline) { BaseProperties?.Update(timeline); TryOrBreak(() => Update(timeline.Delta.TotalSeconds), "Failed to update"); } /// /// Enables the layer brush if it isn't already enabled /// internal void InternalEnable() { if (Enabled) return; if (!TryOrBreak(EnableLayerBrush, "Failed to enable")) return; Enabled = true; } /// /// Disables the layer brush if it isn't already disabled /// internal void InternalDisable() { if (!Enabled) return; DisableLayerBrush(); Enabled = false; } // Not only is this needed to initialize properties on the layer brushes, it also prevents implementing anything // but LayerBrush and RgbNetLayerBrush outside the core internal abstract void Initialize(); internal abstract void InternalRender(SKCanvas canvas, SKRect path, SKPaint paint); internal void Save() { // No need to update the type or provider ID, they're set once by the LayerBrushDescriptors CreateInstance and can't change BaseProperties?.ApplyToEntity(); LayerBrushEntity.PropertyGroup = BaseProperties?.PropertyGroupEntity; } /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #region Implementation of IPluginFeatureDependent /// public IEnumerable GetFeatureDependencies() { IEnumerable result = [Descriptor.Provider]; if (BaseProperties != null) result = result.Concat(BaseProperties.GetFeatureDependencies()); return result; } #endregion } /// /// Describes the type of a layer brush /// public enum LayerBrushType { /// /// A regular brush that users Artemis' SkiaSharp-based rendering engine /// Regular, /// /// An RGB.NET brush that uses RGB.NET's per-LED rendering engine. /// RgbNet }