Browse Source

Group Opacity wip

flabbet 4 years ago
parent
commit
365acdce79

+ 48 - 0
PixiEditor/Helpers/Converters/ActiveItemToOpacityConverter.cs

@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Controls;
+using System.Windows.Data;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Layers.Utils;
+using PixiEditor.ViewModels;
+
+namespace PixiEditor.Helpers.Converters
+{
+    [ValueConversion(typeof(object), typeof(float))]
+    public class ActiveItemToOpacityConverter : IValueConverter
+    {
+
+        public Layer lastLayer //TODO this (ConvertBack)
+        public GuidStructureItem structureItem;
+
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if(value is Layer layer)
+            {
+                return layer.Opacity * 100;
+            }
+            else if(value is GuidStructureItem group)
+            {
+                return group.Opacity;
+            }
+
+            return Binding.DoNothing;
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if (value is Layer layer)
+            {
+                return layer.Opacity * 100;
+            }
+            else if (value is GuidStructureItem group)
+            {
+                return group.Opacity;
+            }
+        }
+    }
+}

+ 0 - 19
PixiEditor/Helpers/Converters/FloatNormalizeConverter.cs

@@ -1,19 +0,0 @@
-using System;
-using System.Globalization;
-using System.Windows.Data;
-
-namespace PixiEditor.Helpers.Converters
-{
-    public class FloatNormalizeConverter : IValueConverter
-    {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            return (float)value * 100;
-        }
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            return (float)value / 100;
-        }
-    }
-}

+ 1 - 1
PixiEditor/Models/Controllers/BitmapManager.cs

@@ -136,7 +136,7 @@ namespace PixiEditor.Models.Controllers
 
 
         public WriteableBitmap GetCombinedLayersBitmap()
         public WriteableBitmap GetCombinedLayersBitmap()
         {
         {
-            return BitmapUtils.CombineLayers(ActiveDocument.Width, ActiveDocument.Height, ActiveDocument.Layers.Where(x => ActiveDocument.GetFinalLayerIsVisible(x)).ToArray());
+            return BitmapUtils.CombineLayers(ActiveDocument.Width, ActiveDocument.Height, ActiveDocument.Layers.Where(x => ActiveDocument.GetFinalLayerIsVisible(x)).ToArray(), ActiveDocument.LayerStructure);
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 1 - 1
PixiEditor/Models/DataHolders/Document/Document.Layers.cs

@@ -81,7 +81,7 @@ namespace PixiEditor.Models.DataHolders
         /// Gets final layer IsVisible taking into consideration group visibility.
         /// Gets final layer IsVisible taking into consideration group visibility.
         /// </summary>
         /// </summary>
         /// <param name="layer">Layer to check.</param>
         /// <param name="layer">Layer to check.</param>
-        /// <returns>True if is visible, false if at least parent is not visible or layer itself is invisible</returns>
+        /// <returns>True if is visible, false if at least parent is not visible or layer itself is invisible.</returns>
         public bool GetFinalLayerIsVisible(Layer layer)
         public bool GetFinalLayerIsVisible(Layer layer)
         {
         {
             if (!layer.IsVisible)
             if (!layer.IsVisible)

+ 123 - 54
PixiEditor/Models/ImageManipulation/BitmapUtils.cs

@@ -1,12 +1,15 @@
-using System;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Layers.Utils;
+using PixiEditor.Models.Position;
+using PixiEditor.Parser;
+using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Runtime.CompilerServices;
 using System.Windows;
 using System.Windows;
 using System.Windows.Media;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Media.Imaging;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.Layers;
-using PixiEditor.Models.Position;
 
 
 namespace PixiEditor.Models.ImageManipulation
 namespace PixiEditor.Models.ImageManipulation
 {
 {
@@ -37,7 +40,7 @@ namespace PixiEditor.Models.ImageManipulation
         /// <param name="height">Height of final bitmap.</param>.
         /// <param name="height">Height of final bitmap.</param>.
         /// <param name="layers">Layers to combine.</param>
         /// <param name="layers">Layers to combine.</param>
         /// <returns>WriteableBitmap of layered bitmaps.</returns>
         /// <returns>WriteableBitmap of layered bitmaps.</returns>
-        public static WriteableBitmap CombineLayers(int width, int height, params Layer[] layers)
+        public static WriteableBitmap CombineLayers(int width, int height, Layer[] layers, LayerStructure structure = null)
         {
         {
             WriteableBitmap finalBitmap = BitmapFactory.New(width, height);
             WriteableBitmap finalBitmap = BitmapFactory.New(width, height);
 
 
@@ -45,33 +48,24 @@ namespace PixiEditor.Models.ImageManipulation
             {
             {
                 for (int i = 0; i < layers.Length; i++)
                 for (int i = 0; i < layers.Length; i++)
                 {
                 {
-                    float layerOpacity = layers[i].Opacity;
+                    float layerOpacity = structure == null ? layers[i].Opacity : LayerStructureUtils.GetFinalLayerOpacity(layers[i], structure);
                     Layer layer = layers[i];
                     Layer layer = layers[i];
 
 
-                    for (int y = 0; y < layers[i].Height; y++)
+                    if (layer.OffsetX < 0 || layer.OffsetY < 0 ||
+                        layer.Width + layer.OffsetX > layer.MaxWidth ||
+                        layer.Height + layer.OffsetY > layer.MaxHeight)
+                    {
+                        throw new InvalidOperationException("Layers must not extend beyond canvas borders");
+                    }
+
+                    for (int y = 0; y < layer.Height; y++)
                     {
                     {
-                        for (int x = 0; x < layers[i].Width; x++)
+                        for (int x = 0; x < layer.Width; x++)
                         {
                         {
+                            Color previousColor = finalBitmap.GetPixel(x + layer.OffsetX, y + layer.OffsetY);
                             Color color = layer.GetPixel(x, y);
                             Color color = layer.GetPixel(x, y);
-                            if (i > 0 && ((color.A < 255 && color.A > 0) || (layerOpacity < 1f && layerOpacity > 0 && color.A > 0)))
-                            {
-                                var lastLayerPixel = finalBitmap.GetPixel(x + layer.OffsetX, y + layer.OffsetY);
-                                byte pixelA = (byte)(color.A * layerOpacity);
-                                byte r = (byte)((color.R * pixelA / 255) + (lastLayerPixel.R * lastLayerPixel.A * (255 - pixelA) / (255 * 255)));
-                                byte g = (byte)((color.G * pixelA / 255) + (lastLayerPixel.G * lastLayerPixel.A * (255 - pixelA) / (255 * 255)));
-                                byte b = (byte)((color.B * pixelA / 255) + (lastLayerPixel.B * lastLayerPixel.A * (255 - pixelA) / (255 * 255)));
-                                byte a = (byte)(pixelA + (lastLayerPixel.A * (255 - pixelA) / 255));
-                                color = Color.FromArgb(a, r, g, b);
-                            }
-                            else
-                            {
-                                color = Color.FromArgb(color.A, color.R, color.G, color.B);
-                            }
-
-                            if (color.A > 0)
-                            {
-                                finalBitmap.SetPixel(x + layer.OffsetX, y + layer.OffsetY, color);
-                            }
+
+                            finalBitmap.SetPixel(x + layer.OffsetX, y + layer.OffsetY, BlendColor(previousColor, color, layerOpacity));
                         }
                         }
                     }
                     }
                 }
                 }
@@ -80,37 +74,64 @@ namespace PixiEditor.Models.ImageManipulation
             return finalBitmap;
             return finalBitmap;
         }
         }
 
 
-      /// <summary>
+        public static Color GetColorAtPointCombined(int x, int y, params Layer[] layers)
+        {
+            Color prevColor = Color.FromArgb(0, 0, 0, 0);
+
+            for (int i = 0; i < layers.Length; i++)
+            {
+                Color color = layers[i].GetPixelWithOffset(x, y);
+                float layerOpacity = layers[i].Opacity;
+
+                prevColor = BlendColor(prevColor, color, layerOpacity);
+            }
+
+            return prevColor;
+        }
+
+        /// <summary>
         /// Generates simplified preview from Document, very fast, great for creating small previews. Creates uniform streched image.
         /// Generates simplified preview from Document, very fast, great for creating small previews. Creates uniform streched image.
         /// </summary>
         /// </summary>
-        /// <param name="document">Document which will be used to generate preview.</param>
+        /// <param name="document">Document which be used to generate preview.</param>
         /// <param name="maxPreviewWidth">Max width of preview.</param>
         /// <param name="maxPreviewWidth">Max width of preview.</param>
         /// <param name="maxPreviewHeight">Max height of preview.</param>
         /// <param name="maxPreviewHeight">Max height of preview.</param>
         /// <returns>WriteableBitmap image.</returns>
         /// <returns>WriteableBitmap image.</returns>
         public static WriteableBitmap GeneratePreviewBitmap(Document document, int maxPreviewWidth, int maxPreviewHeight)
         public static WriteableBitmap GeneratePreviewBitmap(Document document, int maxPreviewWidth, int maxPreviewHeight)
         {
         {
-            return GeneratePreviewBitmap(document.Layers, maxPreviewWidth, maxPreviewHeight, document.Width, document.Height, 0, 0);
+            var opacityLayers = document.Layers.Where(x => x.IsVisible && x.Opacity > 0.8f);
+
+            return GeneratePreviewBitmap(opacityLayers, document.Width, document.Height, maxPreviewWidth, maxPreviewHeight);
         }
         }
 
 
-        /// <summary>
-        /// Generates simplified preview from layers, very fast, great for creating small previews. Creates uniform streched image.
-        /// </summary>
-        /// <param name="layers">Layers which will be used to generate preview.</param>
-        /// <param name="maxPreviewWidth">Max width of preview.</param>
-        /// <param name="maxPreviewHeight">Max height of preview.</param>
-        /// <returns>WriteableBitmap image.</returns>
-        public static WriteableBitmap GeneratePreviewBitmap(IEnumerable<Layer> layers, int maxPreviewWidth, int maxPreviewHeight, bool showHidden = false)
+        public static WriteableBitmap GeneratePreviewBitmap(IEnumerable<Layer> layers, int width, int height, int maxPreviewWidth, int maxPreviewHeight)
         {
         {
-            int minOffsetX = layers.Min(x => x.OffsetX);
-            int minOffsetY = layers.Min(x => x.OffsetY);
-            int width = layers.Max(x => x.OffsetX + x.Width) - minOffsetX;
-            int height = layers.Max(x => x.OffsetY + x.Height) - minOffsetY;
-            return GeneratePreviewBitmap(layers, maxPreviewWidth, maxPreviewHeight, width, height, minOffsetX, minOffsetY, showHidden);
+            return GeneratePreviewBitmap(
+            layers.Select(x => x.LayerBitmap),
+            layers.Select(x => x.OffsetX),
+            layers.Select(x => x.OffsetY),
+            width,
+            height,
+            maxPreviewWidth,
+            maxPreviewHeight);
+        }
+
+        public static WriteableBitmap GeneratePreviewBitmap(IEnumerable<SerializableLayer> layers, int width, int height, int maxPreviewWidth, int maxPreviewHeight)
+        {
+            var opacityLayers = layers.Where(x => x.IsVisible && x.Opacity > 0.8f);
+
+            return GeneratePreviewBitmap(
+                opacityLayers.Select(x => BytesToWriteableBitmap(x.Width, x.Height, x.BitmapBytes)),
+                opacityLayers.Select(x => x.OffsetX),
+                opacityLayers.Select(x => x.OffsetY),
+                width,
+                height,
+                maxPreviewWidth,
+                maxPreviewHeight);
         }
         }
 
 
         public static Dictionary<Guid, Color[]> GetPixelsForSelection(Layer[] layers, Coordinates[] selection)
         public static Dictionary<Guid, Color[]> GetPixelsForSelection(Layer[] layers, Coordinates[] selection)
         {
         {
-            Dictionary<Guid, Color[]> result = new ();
+            Dictionary<Guid, Color[]> result = new();
 
 
             foreach (Layer layer in layers)
             foreach (Layer layer in layers)
             {
             {
@@ -137,23 +158,71 @@ namespace PixiEditor.Models.ImageManipulation
             return result;
             return result;
         }
         }
 
 
-        private static WriteableBitmap GeneratePreviewBitmap(IEnumerable<Layer> layers, int maxPreviewWidth, int maxPreviewHeight, int width, int height, int minOffsetX, int minOffsetY, bool showHidden = false)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private static Color BlendColor(Color previousColor, Color color, float opacity)
+        {
+            if ((color.A < 255 && color.A > 0) || (opacity < 1f && opacity > 0 && color.A > 0))
+            {
+                byte pixelA = (byte)(color.A * opacity);
+                byte r = (byte)((color.R * pixelA / 255) + (previousColor.R * previousColor.A * (255 - pixelA) / (255 * 255)));
+                byte g = (byte)((color.G * pixelA / 255) + (previousColor.G * previousColor.A * (255 - pixelA) / (255 * 255)));
+                byte b = (byte)((color.B * pixelA / 255) + (previousColor.B * previousColor.A * (255 - pixelA) / (255 * 255)));
+                byte a = (byte)(pixelA + (previousColor.A * (255 - pixelA) / 255));
+                color = Color.FromArgb(a, r, g, b);
+            }
+            else
+            {
+                color = Color.FromArgb(color.A, color.R, color.G, color.B);
+            }
+
+            if (color.A > 0)
+            {
+                return color;
+            }
+
+            return previousColor;
+        }
+
+        private static WriteableBitmap GeneratePreviewBitmap(
+            IEnumerable<WriteableBitmap> layerBitmaps,
+            IEnumerable<int> offsetsX,
+            IEnumerable<int> offsetsY,
+            int width,
+            int height,
+            int maxPreviewWidth,
+            int maxPreviewHeight)
         {
         {
+            int count = layerBitmaps.Count();
+
+            if (count != offsetsX.Count() || count != offsetsY.Count())
+            {
+                throw new ArgumentException("There were not the same amount of bitmaps and offsets", nameof(layerBitmaps));
+            }
+
             WriteableBitmap previewBitmap = BitmapFactory.New(width, height);
             WriteableBitmap previewBitmap = BitmapFactory.New(width, height);
 
 
-            // 0.8 because blit doesn't take into consideration layer opacity. Small images with opacity > 80% are simillar enough.
-            foreach (var layer in layers.Where(x => (x.IsVisible || showHidden) && x.Opacity > 0.8f))
+            var layerBitmapsEnumerator = layerBitmaps.GetEnumerator();
+            var offsetsXEnumerator = offsetsX.GetEnumerator();
+            var offsetsYEnumerator = offsetsY.GetEnumerator();
+
+            while (layerBitmapsEnumerator.MoveNext())
             {
             {
+                offsetsXEnumerator.MoveNext();
+                offsetsYEnumerator.MoveNext();
+
+                var bitmap = layerBitmapsEnumerator.Current;
+                var offsetX = offsetsXEnumerator.Current;
+                var offsetY = offsetsYEnumerator.Current;
+
                 previewBitmap.Blit(
                 previewBitmap.Blit(
-                    new Rect(layer.OffsetX - minOffsetX, layer.OffsetY - minOffsetY, layer.Width, layer.Height),
-                    layer.LayerBitmap,
-                    new Rect(0, 0, layer.Width, layer.Height));
+                    new Rect(offsetX, offsetY, bitmap.Width, bitmap.Height),
+                    bitmap,
+                    new Rect(0, 0, bitmap.Width, bitmap.Height));
             }
             }
 
 
-            int finalWidth = width >= height ? maxPreviewWidth : (int)Math.Ceiling(width / ((float)height / maxPreviewHeight));
-            int finalHeight = height > width ? maxPreviewHeight : (int)Math.Ceiling(height / ((float)width / maxPreviewWidth));
-
-            return previewBitmap.Resize(width, height, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
+            int newWidth = width >= height ? maxPreviewWidth : (int)Math.Ceiling(width / ((float)height / maxPreviewHeight));
+            int newHeight = height > width ? maxPreviewHeight : (int)Math.Ceiling(height / ((float)width / maxPreviewWidth));
+            return previewBitmap.Resize(newWidth, newHeight, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
         }
         }
     }
     }
 }
 }

+ 8 - 0
PixiEditor/Models/Layers/GuidStructureItem.cs

@@ -87,6 +87,14 @@ namespace PixiEditor.Models.Layers
             }
             }
         }
         }
 
 
+        private float opacity = 1;
+
+        public float Opacity
+        {
+            get => opacity;
+            set => SetProperty(ref opacity, value);
+        }
+
         public GuidStructureItem(
         public GuidStructureItem(
             string name,
             string name,
             Guid startLayerGuid,
             Guid startLayerGuid,

+ 1 - 1
PixiEditor/Models/Layers/LayerHelper.cs

@@ -70,7 +70,7 @@ namespace PixiEditor.Models.Layers
             int height = yCloser.Height + offsetY + yOther.Height;
             int height = yCloser.Height + offsetY + yOther.Height;
 
 
             // Merge both layers into a bitmap
             // Merge both layers into a bitmap
-            WriteableBitmap mergedBitmap = BitmapUtils.CombineLayers((int)documentsSize.X, (int)documentsSize.Y, thisLayer, otherLayer);
+            WriteableBitmap mergedBitmap = BitmapUtils.CombineLayers((int)documentsSize.X, (int)documentsSize.Y, new Layer[] { thisLayer, otherLayer });
             mergedBitmap = mergedBitmap.Crop(xCloser.OffsetX, yCloser.OffsetY, width, height);
             mergedBitmap = mergedBitmap.Crop(xCloser.OffsetX, yCloser.OffsetY, width, height);
 
 
             // Create the new layer with the merged bitmap
             // Create the new layer with the merged bitmap

+ 36 - 0
PixiEditor/Models/Layers/Utils/LayerStructureUtils.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PixiEditor.Models.Layers.Utils
+{
+    public static class LayerStructureUtils
+    {
+        /// <summary>
+        /// Gets final layer opacity taking into consideration parent groups.
+        /// </summary>
+        /// <param name="layer">Layer to check.</param>
+        /// <returns>Float from range 0-1.</returns>
+        public static float GetFinalLayerOpacity(Layer layer, LayerStructure structure)
+        {
+            if (layer.Opacity == 0)
+            {
+                return 0f;
+            }
+
+            var group = structure.GetGroupByLayer(layer.LayerGuid);
+            GuidStructureItem groupToCheck = group;
+            float finalOpacity = layer.Opacity;
+
+            while (groupToCheck != null)
+            {
+                finalOpacity *= groupToCheck.Opacity;
+                groupToCheck = groupToCheck.Parent;
+            }
+
+            return Math.Clamp(finalOpacity, 0f, 1f);
+        }
+    }
+}

+ 1 - 2
PixiEditor/Views/MainWindow.xaml

@@ -296,8 +296,7 @@
                                     <LayoutAnchorable ContentId="layers" Title="Layers" CanHide="False"
                                     <LayoutAnchorable ContentId="layers" Title="Layers" CanHide="False"
                                                          CanClose="False" CanAutoHide="False"
                                                          CanClose="False" CanAutoHide="False"
                                                          CanDockAsTabbedDocument="True" CanFloat="True">
                                                          CanDockAsTabbedDocument="True" CanFloat="True">
-                                        <usercontrols:LayersManager
-                                            LayerOpacity="{Binding BitmapManager.ActiveDocument.ActiveLayer.OpacityUndoTriggerable, Mode=TwoWay}"
+                                        <usercontrols:LayersManager                                            
                                             LayerCommandsViewModel="{Binding LayersSubViewModel}"
                                             LayerCommandsViewModel="{Binding LayersSubViewModel}"
                                             OpacityInputEnabled="{Binding BitmapManager.ActiveDocument, 
                                             OpacityInputEnabled="{Binding BitmapManager.ActiveDocument, 
                     Converter={StaticResource NotNullToBoolConverter}}">
                     Converter={StaticResource NotNullToBoolConverter}}">

+ 13 - 3
PixiEditor/Views/UserControls/LayerGroupControl.xaml.cs

@@ -50,6 +50,16 @@ namespace PixiEditor.Views.UserControls
         public static readonly DependencyProperty IsVisibleUndoTriggerableProperty =
         public static readonly DependencyProperty IsVisibleUndoTriggerableProperty =
             DependencyProperty.Register("IsVisibleUndoTriggerable", typeof(bool), typeof(LayerGroupControl), new PropertyMetadata(true));
             DependencyProperty.Register("IsVisibleUndoTriggerable", typeof(bool), typeof(LayerGroupControl), new PropertyMetadata(true));
 
 
+        public float GroupOpacity
+        {
+            get { return (float)GetValue(GroupOpacityProperty); }
+            set { SetValue(GroupOpacityProperty, value); }
+        }
+
+        // Using a DependencyProperty as the backing store for GroupOpacity.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty GroupOpacityProperty =
+            DependencyProperty.Register("GroupOpacity", typeof(float), typeof(LayerGroupControl), new PropertyMetadata(1f));
+
         private static void LayersViewModelCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
         private static void LayersViewModelCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
         {
         {
             LayerGroupControl control = (LayerGroupControl)d;
             LayerGroupControl control = (LayerGroupControl)d;
@@ -86,10 +96,11 @@ namespace PixiEditor.Views.UserControls
 
 
         public void GeneratePreviewImage()
         public void GeneratePreviewImage()
         {
         {
-            var layers = LayersViewModel.Owner.BitmapManager.ActiveDocument.LayerStructure.GetGroupLayers(GroupData);
+            var doc = LayersViewModel.Owner.BitmapManager.ActiveDocument;
+            var layers = doc.LayerStructure.GetGroupLayers(GroupData);
             if (layers.Count > 0)
             if (layers.Count > 0)
             {
             {
-                PreviewImage = BitmapUtils.GeneratePreviewBitmap(layers, 25, 25, true);
+                PreviewImage = BitmapUtils.GeneratePreviewBitmap(layers, doc.Width, doc.Height, 25, 25);
             }
             }
         }
         }
 
 
@@ -240,7 +251,6 @@ namespace PixiEditor.Views.UserControls
                 args[1] is bool value
                 args[1] is bool value
                 && doc != null)
                 && doc != null)
             {
             {
-
                 var group = doc.LayerStructure.GetGroupByGuid(groupGuid);
                 var group = doc.LayerStructure.GetGroupByGuid(groupGuid);
 
 
                 group.IsVisible = value;
                 group.IsVisible = value;

+ 10 - 4
PixiEditor/Views/UserControls/LayersManager.xaml

@@ -11,7 +11,7 @@
              mc:Ignorable="d"
              mc:Ignorable="d"
              d:DesignHeight="450" d:DesignWidth="250" Name="layersManager">
              d:DesignHeight="450" d:DesignWidth="250" Name="layersManager">
     <UserControl.Resources>
     <UserControl.Resources>
-        <converters:FloatNormalizeConverter x:Key="FloatNormalizeConverter" />
+        <converters:ActiveItemToOpacityConverter x:Key="ActiveItemToOpacityConverter"/>
         <converters:IndexOfConverter x:Key="IndexOfConverter"/>
         <converters:IndexOfConverter x:Key="IndexOfConverter"/>
     </UserControl.Resources>
     </UserControl.Resources>
     <Grid>
     <Grid>
@@ -52,8 +52,8 @@
                         IsEnabled="{Binding Path=OpacityInputEnabled, ElementName=layersManager}" 
                         IsEnabled="{Binding Path=OpacityInputEnabled, ElementName=layersManager}" 
                         Width="40" Height="20"
                         Width="40" Height="20"
                         VerticalAlignment="Center"
                         VerticalAlignment="Center"
-                        Value="{Binding LayerOpacity, Mode=TwoWay,
-                        Converter={StaticResource FloatNormalizeConverter}, ElementName=layersManager}" />
+                        Value="{Binding ElementName=treeView, Path=SelectedItem,
+                    Converter={StaticResource ActiveItemToOpacityConverter}}" />
                 <Label Content="%" Foreground="White" VerticalAlignment="Center"/>
                 <Label Content="%" Foreground="White" VerticalAlignment="Center"/>
             </StackPanel>
             </StackPanel>
         </DockPanel>
         </DockPanel>
@@ -66,7 +66,13 @@
                 </TreeView.ItemsPanel>
                 </TreeView.ItemsPanel>
                 <TreeView.Resources>
                 <TreeView.Resources>
                     <HierarchicalDataTemplate DataType="{x:Type layers:LayerGroup}" ItemsSource="{Binding Items}">
                     <HierarchicalDataTemplate DataType="{x:Type layers:LayerGroup}" ItemsSource="{Binding Items}">
-                    <local:LayerGroupControl GroupName="{Binding Name}" IsVisibleUndoTriggerable="{Binding StructureData.IsVisible}" LayersViewModel="{Binding LayerCommandsViewModel, ElementName=layersManager}" GroupGuid="{Binding GroupGuid}" GroupData="{Binding StructureData}" MouseMove="LayerGroup_MouseMove"/>
+                    <local:LayerGroupControl GroupName="{Binding Name}" 
+                                             IsVisibleUndoTriggerable="{Binding StructureData.IsVisible}" 
+                                             GroupOpacity="{Binding StructureData.Opacity}"
+                                             LayersViewModel="{Binding LayerCommandsViewModel, ElementName=layersManager}" 
+                                             GroupGuid="{Binding GroupGuid}" 
+                                             GroupData="{Binding StructureData}" 
+                                             MouseMove="LayerGroup_MouseMove"/>
                     </HierarchicalDataTemplate>
                     </HierarchicalDataTemplate>
                     <DataTemplate DataType="{x:Type layers:Layer}">
                     <DataTemplate DataType="{x:Type layers:Layer}">
                         <local:LayerStructureItemContainer                             
                         <local:LayerStructureItemContainer                             

+ 0 - 10
PixiEditor/Views/UserControls/LayersManager.xaml.cs

@@ -25,16 +25,6 @@ namespace PixiEditor.Views.UserControls
                 typeof(LayersManager),
                 typeof(LayersManager),
                 new PropertyMetadata(default(ObservableCollection<object>)));
                 new PropertyMetadata(default(ObservableCollection<object>)));
 
 
-        public float LayerOpacity
-        {
-            get { return (float)GetValue(LayerOpacityProperty); }
-            set { SetValue(LayerOpacityProperty, value); }
-        }
-
-        // Using a DependencyProperty as the backing store for LayerOpacity.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty LayerOpacityProperty =
-            DependencyProperty.Register("LayerOpacity", typeof(float), typeof(LayersManager), new PropertyMetadata(0f));
-
         public LayersViewModel LayerCommandsViewModel
         public LayersViewModel LayerCommandsViewModel
         {
         {
             get { return (LayersViewModel)GetValue(LayerCommandsViewModelProperty); }
             get { return (LayersViewModel)GetValue(LayerCommandsViewModelProperty); }