Pārlūkot izejas kodu

Merge branch 'master' into async-rendering

Krzysztof Krysiński 2 dienas atpakaļ
vecāks
revīzija
db1135db42

+ 1 - 1
src/ColorPicker

@@ -1 +1 @@
-Subproject commit 943e9abbb60b73c4965b947e987dc2696e0b08f8
+Subproject commit f39e02c617d9a4a57b1a911fc4f03bbf7a7fd987

+ 1 - 1
src/Directory.Build.props

@@ -1,7 +1,7 @@
 <Project>
     <PropertyGroup>
         <CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)Custom.ruleset</CodeAnalysisRuleSet>
-		    <AvaloniaVersion>11.3.0</AvaloniaVersion>
+		    <AvaloniaVersion>11.3.5</AvaloniaVersion>
     </PropertyGroup>
   
   <PropertyGroup Condition="$([MSBuild]::IsOsPlatform('Windows')) AND '$(Platform)' == 'x64'">

+ 1 - 1
src/Drawie

@@ -1 +1 @@
-Subproject commit 71aee038d0cea1f40e12250f90ef65eb7ab21d42
+Subproject commit adfaa90105229e3183e3049276af982f3e5b1b5d

+ 1 - 1
src/PixiDocks

@@ -1 +1 @@
-Subproject commit 6e745d0309ad7a00a53f62f2aa362be77903a5fd
+Subproject commit af479aac479fd1a0494cb61fa49a03a3713c96cf

+ 166 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Effects/PosterizationNode.cs

@@ -0,0 +1,166 @@
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Shaders;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.ImageData;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Changeables.Graph.ColorSpaces;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Rendering;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Effects;
+
+[NodeInfo("Posterization")]
+public class PosterizationNode : RenderNode, IRenderInput
+{
+    public RenderInputProperty Background { get; }
+    public InputProperty<PosterizationMode> Mode { get; }
+    public InputProperty<int> Levels { get; }
+    public InputProperty<ColorSpaceType> PosterizationColorSpace { get; }
+    
+    private Paint paint;
+    private Shader shader;
+    
+    private Shader? lastImageShader;
+    private VecI lastDocumentSize;
+    
+    private string shaderCode = """
+                                 uniform shader iImage;
+                                 uniform int iMode;
+                                 uniform float iLevels;
+                                 
+                                 half posterize(half value, float levels) {
+                                     return clamp(floor(value * (levels - 1) + 0.5) / (levels - 1), 0.0, 1.0);
+                                 }
+                                 
+                                 half4 posterizeRgb(half4 color, float levels) {
+                                     return half4(
+                                         posterize(color.r, levels),
+                                         posterize(color.g, levels),
+                                         posterize(color.b, levels),
+                                         color.a
+                                     );
+                                 }
+                                 
+                                 half4 posterizeLuminance(half4 color, float levels) {
+                                     half lum = dot(color.rgb, half3(0.299, 0.587, 0.114));
+                                     half posterizedLum = posterize(lum, levels);
+                                     return half4(posterizedLum, posterizedLum, posterizedLum, color.a);
+                                 }
+                                 
+                                 half4 main(float2 uv)
+                                 {
+                                    half4 color = iImage.eval(uv);
+                                    half4 result;
+                                    
+                                    if(iMode == 0) {
+                                        result = posterizeRgb(color, iLevels);
+                                    } else if(iMode == 1) {
+                                        result = posterizeLuminance(color, iLevels);
+                                    } 
+                                    
+                                    return result;
+                                 }
+                                 """;
+
+    protected override bool ExecuteOnlyOnCacheChange => true;
+
+    public PosterizationNode()
+    {
+        Background = CreateRenderInput("Background", "BACKGROUND");
+        Mode = CreateInput("Mode", "MODE", PosterizationMode.Rgb);
+        Levels = CreateInput("Levels", "LEVELS", 8)
+            .WithRules(v => v.Min(2).Max(256));
+        PosterizationColorSpace = CreateInput("PosterizationColorSpace", "COLOR_SPACE", ColorSpaceType.Srgb);
+        
+        paint = new Paint();
+        paint.BlendMode = BlendMode.Src;
+        Output.FirstInChain = null;
+    }
+
+    protected override void OnExecute(RenderContext context)
+    {
+        base.OnExecute(context);
+        lastDocumentSize = context.RenderOutputSize;
+        
+        Uniforms uniforms = new Uniforms();
+        uniforms.Add("iImage", new Uniform("iImage", lastImageShader));
+        uniforms.Add("iMode", new Uniform("iMode", (int)Mode.Value));
+        uniforms.Add("iLevels", new Uniform("iLevels", (float)Levels.Value));
+        shader?.Dispose();
+        shader = Shader.Create(shaderCode, uniforms, out _);
+    }
+    
+    protected override void OnPaint(RenderContext context, DrawingSurface surface)
+    {
+        if (Background.Value == null)
+        {
+            return;
+        }
+
+        ColorSpace colorSpace;
+        switch (PosterizationColorSpace.Value)
+        {
+            case ColorSpaceType.Srgb:
+                colorSpace = ColorSpace.CreateSrgb();
+                break;
+            case ColorSpaceType.LinearSrgb:
+                colorSpace = ColorSpace.CreateSrgbLinear();
+                break;
+            case ColorSpaceType.Inherit:
+                colorSpace = context.ProcessingColorSpace;
+                break;
+            default:
+                colorSpace = ColorSpace.CreateSrgb();
+                break;
+        }
+        
+        using Texture temp = Texture.ForProcessing(surface, colorSpace);
+        Background.Value.Paint(context, temp.DrawingSurface);
+        var snapshot = temp.DrawingSurface.Snapshot();
+        
+        lastImageShader?.Dispose();
+        lastImageShader = snapshot.ToShader();
+
+        Uniforms uniforms = new Uniforms();
+        uniforms.Add("iImage", new Uniform("iImage", lastImageShader));
+        uniforms.Add("iMode", new Uniform("iMode", (int)Mode.Value));
+        uniforms.Add("iLevels", new Uniform("iLevels", (float)Levels.Value));
+        shader = shader.WithUpdatedUniforms(uniforms);
+        paint.Shader = shader;
+        snapshot.Dispose();
+        
+        var savedTemp = temp.DrawingSurface.Canvas.Save();
+        temp.DrawingSurface.Canvas.SetMatrix(Matrix3X3.Identity);
+        temp.DrawingSurface.Canvas.DrawRect(0, 0, context.RenderOutputSize.X, context.RenderOutputSize.Y, paint);
+        temp.DrawingSurface.Canvas.RestoreToCount(savedTemp);
+        
+        var saved = surface.Canvas.Save();
+        surface.Canvas.SetMatrix(Matrix3X3.Identity);
+        surface.Canvas.DrawSurface(temp.DrawingSurface, 0, 0);
+        surface.Canvas.RestoreToCount(saved);
+    }
+    
+    public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
+    {
+        return new RectD(0, 0, lastDocumentSize.X, lastDocumentSize.Y);
+    }
+
+    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
+    {
+        OnPaint(context, renderOn);
+        return true;
+    }
+
+    public override Node CreateCopy()
+    {
+        return new PosterizationNode();
+    }
+}
+
+public enum PosterizationMode
+{
+    Rgb = 0,
+    Luminance = 1,
+}

+ 4 - 0
src/PixiEditor/Data/Localization/Languages/en.json

@@ -848,6 +848,10 @@
     "TYPE": "Type",
     "EFFECTS": "Effects",
     "OUTLINE_NODE": "Outline",
+    "POSTERIZATION_NODE": "Posterize",
+    "RGB_POSTERIZATION_MODE": "RGB",
+    "LUMINANCE_POSTERIZATION_MODE": "Luminance",
+    "LEVELS": "Levels",
     "SHADER_CODE": "Shader Code",
     "SHADER_NODE": "Shader",
     "FAILED_TO_OPEN_EDITABLE_STRING_TITLE": "Failed to open file",

+ 3 - 0
src/PixiEditor/Models/EnumTranslations.cs

@@ -99,6 +99,9 @@ using BlendMode = PixiEditor.ChangeableDocument.Enums.BlendMode;
 [assembly: LocalizeEnum<OutlineType>(OutlineType.Gaussian, "GAUSSIAN_OUTLINE_TYPE")]
 [assembly: LocalizeEnum<OutlineType>(OutlineType.PixelPerfect, "PIXEL_PERFECT_OUTLINE_TYPE")]
 
+[assembly: LocalizeEnum<PosterizationMode>(PosterizationMode.Rgb, "RGB_POSTERIZATION_MODE")]
+[assembly: LocalizeEnum<PosterizationMode>(PosterizationMode.Luminance, "LUMINANCE_POSTERIZATION_MODE")]
+
 [assembly: LocalizeEnum<VoronoiFeature>(VoronoiFeature.F1, "F1_VORONOI_FEATURE")]
 [assembly: LocalizeEnum<VoronoiFeature>(VoronoiFeature.F2, "F2_VORONOI_FEATURE")]
 [assembly: LocalizeEnum<VoronoiFeature>(VoronoiFeature.F2MinusF1, "F2_MINUS_F1_VORONOI_FEATURE")]

+ 11 - 0
src/PixiEditor/ViewModels/Document/Nodes/Effects/PosterizationNodeViewModel.cs

@@ -0,0 +1,11 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Effects;
+using PixiEditor.ViewModels.Nodes;
+
+namespace PixiEditor.ViewModels.Document.Nodes.Effects;
+
+[NodeViewModel("POSTERIZATION_NODE", "EFFECTS", PixiPerfectIcons.Swatches)]
+internal class PosterizationNodeViewModel : NodeViewModel<PosterizationNode>
+{
+    
+}
+

+ 19 - 2
src/PixiEditor/ViewModels/SubViewModels/UserViewModel.cs

@@ -54,12 +54,17 @@ internal class UserViewModel : SubViewModel<ViewModelMain>
     {
         get
         {
-            if (TimeToEndTimeout == null)
+            if (TimeToEndTimeout == null || !EmailEqualsLastSentMail)
             {
                 return string.Empty;
             }
 
             TimeSpan timeLeft = TimeToEndTimeout.Value - DateTime.Now;
+            if(timeLeft.TotalHours > 1)
+                return $"({timeLeft:hh\\:mm\\:ss})";
+            if(timeLeft.TotalMinutes > 1)
+                return $"({timeLeft:mm\\:ss})";
+
             return timeLeft.TotalSeconds > 0 ? $"({timeLeft:ss})" : string.Empty;
         }
     }
@@ -194,6 +199,7 @@ internal class UserViewModel : SubViewModel<ViewModelMain>
             LastError = null;
             try
             {
+                lastSentHash = EmailUtility.GetEmailHash(email);
                 await pixiAuthIdentityProvider.RequestLogin(email);
             }
             catch (Exception ex)
@@ -206,7 +212,7 @@ internal class UserViewModel : SubViewModel<ViewModelMain>
     public bool CanRequestLogin(string email)
     {
         return IdentityProvider is PixiAuthIdentityProvider && !string.IsNullOrEmpty(email) && email.Contains('@') &&
-               !HasTimeout();
+               !(HasTimeout() && EmailEqualsLastSentMail);
     }
 
     public async Task ResendActivation(string email)
@@ -240,6 +246,10 @@ internal class UserViewModel : SubViewModel<ViewModelMain>
         DispatcherTimer.RunOnce(
             () =>
             {
+                if (TimeToEndTimeout.HasValue && TimeToEndTimeout.Value > DateTime.Now)
+                {
+                    return;
+                }
                 TimeToEndTimeout = null;
                 LastError = null;
                 NotifyProperties();
@@ -376,6 +386,13 @@ internal class UserViewModel : SubViewModel<ViewModelMain>
 
     private void OnError(string error, object? arg = null)
     {
+        if (error != "TOO_MANY_REQUESTS")
+        {
+            TimeToEndTimeout = null;
+            timerCancelable?.Dispose();
+            timerCancelable = null;
+            NotifyProperties();
+        }
         if (error == "SESSION_NOT_VALIDATED")
         {
             LastError = null;

+ 1 - 1
tests/Directory.Build.props

@@ -1,7 +1,7 @@
 <Project>
     <PropertyGroup>
         <CodeAnalysisRuleSet>../Custom.ruleset</CodeAnalysisRuleSet>
-		<AvaloniaVersion>11.3.0</AvaloniaVersion>
+		<AvaloniaVersion>11.3.5</AvaloniaVersion>
     </PropertyGroup>
     <ItemGroup>
         <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />