Browse Source

Clip to background

flabbet 1 year ago
parent
commit
f8a2e210df

+ 2 - 2
src/ChunkyImageLib/Operations/OperationHelper.cs

@@ -22,7 +22,7 @@ public static class OperationHelper
     /// <summary>
     /// toModify[x,y].Alpha = Math.Min(toModify[x,y].Alpha, toGetAlphaFrom[x,y].Alpha)
     /// </summary>
-    public static unsafe void ClampAlpha(DrawingSurface toModify, DrawingSurface toGetAlphaFrom, RectI? clippingRect = null)
+    public static unsafe void ClampAlpha(IPixelsMap toModify, IPixelsMap toGetAlphaFrom, RectI? clippingRect = null)
     {
         if (clippingRect is not null)
         {
@@ -59,7 +59,7 @@ public static class OperationHelper
         }
     }
 
-    private static unsafe void ClampAlphaWithClippingRect(DrawingSurface toModify, DrawingSurface toGetAlphaFrom, RectI clippingRect)
+    private static unsafe void ClampAlphaWithClippingRect(IPixelsMap toModify, IPixelsMap toGetAlphaFrom, RectI clippingRect)
     {
         using Pixmap map = toModify.PeekPixels();
         using Pixmap refMap = toGetAlphaFrom.PeekPixels();

+ 2 - 2
src/PixiEditor.AvaloniaUI/ViewModels/Document/DocumentViewModel.Serialization.cs

@@ -112,7 +112,7 @@ internal partial class DocumentViewModel
             BlendMode = (BlendMode)(int)folder.BlendMode.Value,
             Enabled = folder.IsVisible.Value,
             Opacity = folder.Opacity.Value,
-            ClipToMemberBelow = folder.ClipToMemberBelow.Value,
+            ClipToMemberBelow = folder.ClipToPreviousMember.Value,
             Mask = GetMask(folder.Mask.Value, folder.MaskIsVisible.Value)
         };
     }
@@ -128,7 +128,7 @@ internal partial class DocumentViewModel
         {
             Width = result?.Width ?? 0, Height = result?.Height ?? 0, OffsetX = tightBounds?.X ?? 0, OffsetY = tightBounds?.Y ?? 0,
             Enabled = layer.IsVisible.Value, BlendMode = (BlendMode)(int)layer.BlendMode.Value, ImageBytes = bytes,
-            ClipToMemberBelow = layer.ClipToMemberBelow.Value, Name = layer.MemberName,
+            ClipToMemberBelow = layer.ClipToPreviousMember.Value, Name = layer.MemberName,
             Guid = layer.Id,
             LockAlpha = layer is ITransparencyLockable { LockTransparency: true },
             Opacity = layer.Opacity.Value, Mask = GetMask(layer.Mask.Value, layer.MaskIsVisible.Value)

+ 1 - 1
src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/CreateFolder_ChangeInfo.cs

@@ -30,7 +30,7 @@ public record class CreateFolder_ChangeInfo : CreateStructureMember_ChangeInfo
             parentGuid,
             folder.Opacity.Value,
             folder.IsVisible.Value,
-            folder.ClipToMemberBelow.Value,
+            folder.ClipToPreviousMember.Value,
             folder.MemberName,
             folder.BlendMode.Value,
             folder.Id,

+ 1 - 1
src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/CreateLayer_ChangeInfo.cs

@@ -36,7 +36,7 @@ public record class CreateLayer_ChangeInfo : CreateStructureMember_ChangeInfo
             parentGuid,
             layer.Opacity.Value,
             layer.IsVisible.Value,
-            layer.ClipToMemberBelow.Value,
+            layer.ClipToPreviousMember.Value,
             layer.MemberName,
             layer.BlendMode.Value,
             layer.Id,

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyStructureNode.cs

@@ -8,7 +8,7 @@ public interface IReadOnlyStructureNode : IReadOnlyNode
 {
     public InputProperty<float> Opacity { get; }
     public InputProperty<bool> IsVisible { get; }
-    public InputProperty<bool> ClipToMemberBelow { get; }
+    public InputProperty<bool> ClipToPreviousMember { get; }
     public InputProperty<BlendMode> BlendMode { get; }
     public InputProperty<ChunkyImage?> Mask { get; }
     public InputProperty<bool> MaskIsVisible { get; }

+ 12 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs

@@ -1,4 +1,5 @@
-using PixiEditor.ChangeableDocument.Changeables.Animations;
+using ChunkyImageLib.Operations;
+using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
@@ -62,6 +63,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
                 ChunkResolution.Full, workingSurface.DrawingSurface, VecI.Zero, blendPaint);
             
             ApplyMaskIfPresent(workingSurface);
+            ApplyRasterClip(workingSurface);
         }
         else
         {
@@ -72,6 +74,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
                 ChunkResolution.Full, workingSurface.DrawingSurface, VecI.Zero, blendPaint);
             
             ApplyMaskIfPresent(workingSurface);
+            ApplyRasterClip(workingSurface);
         }
 
 
@@ -85,6 +88,14 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
     {
         return Mask.Value != null && MaskIsVisible.Value && !Mask.Value.LatestOrCommittedChunkExists();
     }
+    
+    private void ApplyRasterClip(Surface surface)
+    {
+        if (ClipToPreviousMember.Value)
+        {
+            OperationHelper.ClampAlpha(surface.DrawingSurface, Background.Value);
+        }
+    }
 
     private ImageFrame GetFrameImage(KeyFrameTime frame)
     {

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/StructureNode.cs

@@ -11,7 +11,7 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
     public InputProperty<Image?> Background { get; }
     public InputProperty<float> Opacity { get; } 
     public InputProperty<bool> IsVisible { get; }
-    public InputProperty<bool> ClipToMemberBelow { get; }
+    public InputProperty<bool> ClipToPreviousMember { get; }
     public InputProperty<BlendMode> BlendMode { get; } 
     public InputProperty<ChunkyImage?> Mask { get; }
     public InputProperty<bool> MaskIsVisible { get; }
@@ -25,7 +25,7 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
         Background = CreateInput<Image?>("Background", "BACKGROUND", null);
         Opacity = CreateInput<float>("Opacity", "OPACITY", 1);
         IsVisible = CreateInput<bool>("IsVisible", "IS_VISIBLE", true);
-        ClipToMemberBelow = CreateInput<bool>("ClipToMemberBelow", "CLIP_TO_MEMBER_BELOW", false);
+        ClipToPreviousMember = CreateInput<bool>("ClipToMemberBelow", "CLIP_TO_MEMBER_BELOW", false);
         BlendMode = CreateInput<BlendMode>("BlendMode", "BLEND_MODE", Enums.BlendMode.Normal);
         Mask = CreateInput<ChunkyImage?>("Mask", "MASK", null);
         MaskIsVisible = CreateInput<bool>("MaskIsVisible", "MASK_IS_VISIBLE", true);

+ 4 - 4
src/PixiEditor.ChangeableDocument/Changes/Properties/LayerStructure/StructureMemberClipToMemberBelow_Change.cs

@@ -16,16 +16,16 @@ internal class StructureMemberClipToMemberBelow_Change : Change
 
     public override bool InitializeAndValidate(Document target)
     {
-        if (!target.TryFindMember(memberGuid, out var member) || member.ClipToMemberBelow.NonOverridenValue == newValue)
+        if (!target.TryFindMember(memberGuid, out var member) || member.ClipToPreviousMember.NonOverridenValue == newValue)
             return false;
-        originalValue = member.ClipToMemberBelow.NonOverridenValue;
+        originalValue = member.ClipToPreviousMember.NonOverridenValue;
         return true;
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply, out bool ignoreInUndo)
     {
         var member = target.FindMemberOrThrow(memberGuid);
-        member.ClipToMemberBelow.NonOverridenValue = newValue;
+        member.ClipToPreviousMember.NonOverridenValue = newValue;
         ignoreInUndo = false;
         return new StructureMemberClipToMemberBelow_ChangeInfo(memberGuid, newValue);
     }
@@ -33,7 +33,7 @@ internal class StructureMemberClipToMemberBelow_Change : Change
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
     {
         var member = target.FindMemberOrThrow(memberGuid);
-        member.ClipToMemberBelow.NonOverridenValue = originalValue;
+        member.ClipToPreviousMember.NonOverridenValue = originalValue;
         return new StructureMemberClipToMemberBelow_ChangeInfo(memberGuid, originalValue);
     }
 

+ 1 - 0
src/PixiEditor.DrawingApi.Core/Bridge/Operations/IImageImplementation.cs

@@ -21,5 +21,6 @@ namespace PixiEditor.DrawingApi.Core.Bridge.Operations
         public int GetHeight(IntPtr objectPointer);
         public object GetNativeImage(IntPtr objectPointer);
         public Image Clone(Image image);
+        public Pixmap PeekPixels(Image image);
     }
 }

+ 1 - 1
src/PixiEditor.DrawingApi.Core/Surface/DrawingSurface.cs

@@ -7,7 +7,7 @@ using PixiEditor.Numerics;
 
 namespace PixiEditor.DrawingApi.Core.Surface
 {
-    public class DrawingSurface : NativeObject
+    public class DrawingSurface : NativeObject, IPixelsMap
     {
         public override object Native => DrawingBackendApi.Current.SurfaceImplementation.GetNativeSurface(ObjectPointer);
         public Canvas Canvas { get; private set; }

+ 8 - 0
src/PixiEditor.DrawingApi.Core/Surface/IPixelsMap.cs

@@ -0,0 +1,8 @@
+using System;
+
+namespace PixiEditor.DrawingApi.Core.Surface;
+
+public interface IPixelsMap
+{
+    public Pixmap PeekPixels();
+}

+ 6 - 1
src/PixiEditor.DrawingApi.Core/Surface/ImageData/Image.cs

@@ -12,7 +12,7 @@ namespace PixiEditor.DrawingApi.Core.Surface.ImageData
     ///     <para />
     ///     <para>An image always has a non-zero dimensions. If there is a request to create a new image, either directly or via a surface, and either of the requested dimensions are zero, then <see langword="null" /> will be returned.</para>
     /// </remarks>
-    public class Image : NativeObject, ICloneable
+    public class Image : NativeObject, ICloneable, IPixelsMap
     {
         public override object Native => DrawingBackendApi.Current.ImageImplementation.GetNativeImage(ObjectPointer);
 
@@ -45,6 +45,11 @@ namespace PixiEditor.DrawingApi.Core.Surface.ImageData
         {
             return DrawingBackendApi.Current.ImageImplementation.FromPixelCopy(info, pixels);
         }
+        
+        public Pixmap PeekPixels()
+        {
+            return DrawingBackendApi.Current.ImageImplementation.PeekPixels(this);
+        }
 
         public ImgData Encode()
         {

+ 13 - 1
src/PixiEditor.DrawingApi.Skia/Implementations/SkiaImageImplementation.cs

@@ -13,10 +13,15 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
     {
         private readonly SkObjectImplementation<SKData> _imgImplementation;
         private SkObjectImplementation<SKSurface>? _surfaceImplementation;
+        private SkiaPixmapImplementation _pixmapImplementation;
+        private SkiaColorSpaceImplementation colorSpaceImpl;
         
-        public SkiaImageImplementation(SkObjectImplementation<SKData> imgDataImplementation)
+        public SkiaImageImplementation(
+            SkObjectImplementation<SKData> imgDataImplementation, 
+            SkiaPixmapImplementation pixmapImplementation)
         {
             _imgImplementation = imgDataImplementation;
+            _pixmapImplementation = pixmapImplementation;
         }
         
         public void SetSurfaceImplementation(SkObjectImplementation<SKSurface> surfaceImplementation)
@@ -75,6 +80,13 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
             ManagedInstances[nativeImg.Handle] = nativeImg;
             return new Image(nativeImg.Handle);
         }
+        
+        public Pixmap PeekPixels(Image image)
+        {
+            var native = ManagedInstances[image.ObjectPointer];
+            var pixmap = native.PeekPixels();
+            return _pixmapImplementation.CreateFrom(pixmap);
+        }
 
         public void GetColorShifts(ref int platformColorAlphaShift, ref int platformColorRedShift, ref int platformColorGreenShift,
             ref int platformColorBlueShift)

+ 7 - 7
src/PixiEditor.DrawingApi.Skia/SkiaDrawingBackend.cs

@@ -28,7 +28,13 @@ namespace PixiEditor.DrawingApi.Skia
             SkiaImgDataImplementation dataImpl = new SkiaImgDataImplementation();
             ImgDataImplementation = dataImpl;
             
-            SkiaImageImplementation imgImpl = new SkiaImageImplementation(dataImpl);
+            SkiaColorSpaceImplementation colorSpaceImpl = new SkiaColorSpaceImplementation();
+            ColorSpaceImplementation = colorSpaceImpl;
+
+            SkiaPixmapImplementation pixmapImpl = new SkiaPixmapImplementation(colorSpaceImpl);
+            PixmapImplementation = pixmapImpl;
+            
+            SkiaImageImplementation imgImpl = new SkiaImageImplementation(dataImpl, pixmapImpl);
             ImageImplementation = imgImpl;
             
             SkiaColorFilterImplementation colorFilterImpl = new SkiaColorFilterImplementation();
@@ -45,12 +51,6 @@ namespace PixiEditor.DrawingApi.Skia
             
             MatrixImplementation = new SkiaMatrixImplementation();
             
-            SkiaColorSpaceImplementation colorSpaceImpl = new SkiaColorSpaceImplementation();
-            ColorSpaceImplementation = colorSpaceImpl;
-
-            SkiaPixmapImplementation pixmapImpl = new SkiaPixmapImplementation(colorSpaceImpl);
-            PixmapImplementation = pixmapImpl;
-            
             SkiaBitmapImplementation bitmapImpl = new SkiaBitmapImplementation(imgImpl);
             BitmapImplementation = bitmapImpl;