Browse Source

Export preview

flabbet 1 year ago
parent
commit
b7e7473150

+ 1 - 0
src/PixiEditor.AvaloniaUI/ViewModels/Document/DocumentViewModel.cs

@@ -355,6 +355,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
         lastChangeOnSave = Guid.NewGuid();
         lastChangeOnSave = Guid.NewGuid();
         OnPropertyChanged(nameof(AllChangesSaved));
         OnPropertyChanged(nameof(AllChangesSaved));
     }
     }
+    
 
 
     /// <summary>
     /// <summary>
     /// Tries rendering the whole document
     /// Tries rendering the whole document

+ 2 - 1
src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/FileViewModel.cs

@@ -362,7 +362,8 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
             if (doc is null)
             if (doc is null)
                 return;
                 return;
 
 
-            ExportFileDialog info = new ExportFileDialog(MainWindow.Current, doc.SizeBindable) { SuggestedName = Path.GetFileNameWithoutExtension(doc.FileName) };
+            ExportFileDialog info = new ExportFileDialog(MainWindow.Current, doc.SizeBindable, doc) 
+                { SuggestedName = Path.GetFileNameWithoutExtension(doc.FileName) };
             if (await info.ShowDialog())
             if (await info.ShowDialog())
             {
             {
                 SaveResult result = Exporter.TrySaveUsingDataFromDialog(doc, info.FilePath, info.ChosenFormat, out string finalPath, new(info.FileWidth, info.FileHeight));
                 SaveResult result = Exporter.TrySaveUsingDataFromDialog(doc, info.FilePath, info.ChosenFormat, out string finalPath, new(info.FileWidth, info.FileHeight));

+ 6 - 2
src/PixiEditor.AvaloniaUI/Views/Dialogs/ExportFileDialog.cs

@@ -2,6 +2,7 @@
 using Avalonia.Controls;
 using Avalonia.Controls;
 using PixiEditor.AvaloniaUI.Models.Dialogs;
 using PixiEditor.AvaloniaUI.Models.Dialogs;
 using PixiEditor.AvaloniaUI.Models.Files;
 using PixiEditor.AvaloniaUI.Models.Files;
+using PixiEditor.AvaloniaUI.ViewModels.Document;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Numerics;
 using PixiEditor.Numerics;
 
 
@@ -18,11 +19,14 @@ internal class ExportFileDialog : CustomDialog
     private int fileWidth;
     private int fileWidth;
 
 
     private string suggestedName;
     private string suggestedName;
+    
+    private DocumentViewModel document;
 
 
-    public ExportFileDialog(Window owner, VecI size) : base(owner)
+    public ExportFileDialog(Window owner, VecI size, DocumentViewModel doc) : base(owner)
     {
     {
         FileWidth = size.X;
         FileWidth = size.X;
         FileHeight = size.Y;
         FileHeight = size.Y;
+        document = doc;
     }
     }
 
 
     public int FileWidth
     public int FileWidth
@@ -86,7 +90,7 @@ internal class ExportFileDialog : CustomDialog
     }
     }
     public override async Task<bool> ShowDialog()
     public override async Task<bool> ShowDialog()
     {
     {
-        ExportFilePopup popup = new ExportFilePopup(FileWidth, FileHeight) { SuggestedName = SuggestedName };
+        ExportFilePopup popup = new ExportFilePopup(FileWidth, FileHeight, document) { SuggestedName = SuggestedName };
         bool result = await popup.ShowDialog<bool>(OwnerWindow);
         bool result = await popup.ShowDialog<bool>(OwnerWindow);
 
 
         if (result)
         if (result)

+ 69 - 28
src/PixiEditor.AvaloniaUI/Views/Dialogs/ExportFilePopup.axaml

@@ -1,41 +1,82 @@
 <dialogs:PixiEditorPopup xmlns="https://github.com/avaloniaui"
 <dialogs:PixiEditorPopup xmlns="https://github.com/avaloniaui"
-        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-        xmlns:dialogs="clr-namespace:PixiEditor.AvaloniaUI.Views.Dialogs"
-        xmlns:input="clr-namespace:PixiEditor.AvaloniaUI.Views.Input"
-        xmlns:ui1="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
-        CanResize="False"
-        CanMinimize="False"
-        SizeToContent="WidthAndHeight"
-        Name="saveFilePopup"
-        x:Class="PixiEditor.AvaloniaUI.Views.Dialogs.ExportFilePopup"
-        x:ClassModifier="internal"
-        Title="EXPORT_IMAGE">
+                         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+                         xmlns:dialogs="clr-namespace:PixiEditor.AvaloniaUI.Views.Dialogs"
+                         xmlns:input="clr-namespace:PixiEditor.AvaloniaUI.Views.Input"
+                         xmlns:ui1="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
+                         xmlns:visuals="clr-namespace:PixiEditor.AvaloniaUI.Views.Visuals"
+                         CanResize="False"
+                         CanMinimize="False"
+                         SizeToContent="WidthAndHeight"
+                         Name="saveFilePopup"
+                         x:Class="PixiEditor.AvaloniaUI.Views.Dialogs.ExportFilePopup"
+                         x:ClassModifier="internal"
+                         Title="EXPORT_IMAGE">
     <DockPanel Background="{DynamicResource ThemeBackgroundBrush}">
     <DockPanel Background="{DynamicResource ThemeBackgroundBrush}">
         <Button DockPanel.Dock="Bottom" HorizontalAlignment="Center" IsDefault="True"
         <Button DockPanel.Dock="Bottom" HorizontalAlignment="Center" IsDefault="True"
-                Margin="15" ui1:Translator.Key="EXPORT" Command="{Binding ExportCommand, ElementName=saveFilePopup}" />
+                ui1:Translator.Key="EXPORT" Command="{Binding ExportCommand, ElementName=saveFilePopup}" />
 
 
-        <Border HorizontalAlignment="Center" Margin="15,30,15,0" Background="{DynamicResource ThemeBackgroundBrush1}"
-                    VerticalAlignment="Stretch" CornerRadius="10">
-                    <Grid MinHeight="205" MinWidth="240">
+        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Stretch"  Orientation="Vertical"
+                    Margin="0,15,0,0">
+            <StackPanel Spacing="5" Orientation="Horizontal" HorizontalAlignment="Center">
+                <RadioButton GroupName="ExportType" Content="Image" IsChecked="True" IsDefault="True"/>
+                <RadioButton GroupName="ExportType" Content="Animation" />
+                <RadioButton GroupName="ExportType" Content="SpriteSheet" />
+            </StackPanel>
+
+            <Border Margin="15, 30" Padding="10"
+                    Background="{DynamicResource ThemeBackgroundBrush1}"
+                    CornerRadius="{DynamicResource ControlCornerRadius}">
+                <Grid MinHeight="205" MinWidth="400">
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="*" />
+                        <ColumnDefinition Width="160"/>
+                    </Grid.ColumnDefinitions>
+                    <Grid>
                         <Grid.RowDefinitions>
                         <Grid.RowDefinitions>
-                            <RowDefinition/>
-                            <RowDefinition Height="Auto"/>
+                            <RowDefinition />
+                            <RowDefinition Height="Auto" />
                         </Grid.RowDefinitions>
                         </Grid.RowDefinitions>
-                        <input:SizePicker Margin="0,15,0,0"
-                                          x:Name="sizePicker"
-                                          IsSizeUnitSelectionVisible="True"
-                                          VerticalAlignment="Top"
-                                          ChosenHeight="{Binding Path=SaveHeight, Mode=TwoWay, ElementName=saveFilePopup}"
-                                          ChosenWidth="{Binding Path=SaveWidth, Mode=TwoWay, ElementName=saveFilePopup}" />
-                        <TextBlock Grid.Row="1" Margin="5,0,5,10" VerticalAlignment="Bottom" Classes="hyperlink" TextWrapping="Wrap"
-                                   Width="220" TextAlignment="Center" Text="{Binding SizeHint, Mode=OneTime, ElementName=saveFilePopup}">
+                        <input:SizePicker
+                            x:Name="sizePicker"
+                            IsSizeUnitSelectionVisible="True"
+                            VerticalAlignment="Top"
+                            ChosenHeight="{Binding Path=SaveHeight, Mode=TwoWay, ElementName=saveFilePopup}"
+                            ChosenWidth="{Binding Path=SaveWidth, Mode=TwoWay, ElementName=saveFilePopup}" />
+                        <TextBlock Grid.Row="1" Margin="5, 0" VerticalAlignment="Bottom" Classes="hyperlink"
+                                   TextWrapping="Wrap"
+                                   Width="220" TextAlignment="Center"
+                                   Text="{Binding SizeHint, Mode=OneTime, ElementName=saveFilePopup}">
                             <Interaction.Behaviors>
                             <Interaction.Behaviors>
                                 <EventTriggerBehavior EventName="PointerPressed">
                                 <EventTriggerBehavior EventName="PointerPressed">
-                                    <InvokeCommandAction Command="{Binding SetBestPercentageCommand, ElementName=saveFilePopup}"/>
+                                    <InvokeCommandAction
+                                        Command="{Binding SetBestPercentageCommand, ElementName=saveFilePopup}" />
                                 </EventTriggerBehavior>
                                 </EventTriggerBehavior>
                             </Interaction.Behaviors>
                             </Interaction.Behaviors>
                         </TextBlock>
                         </TextBlock>
                     </Grid>
                     </Grid>
-        </Border>
+                    <Grid Grid.Column="1">
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="30"/>
+                            <RowDefinition Height="Auto"/>
+                        </Grid.RowDefinitions>
+                        
+                        <TextBlock Text="Export Preview"/>
+                        <Border Grid.Row="1" BorderThickness="1" Height="200" Width="150">
+                            <Border RenderOptions.BitmapInterpolationMode="None">
+                                <visuals:SurfaceControl x:Name="surfaceControl"
+                                                        Surface="{Binding ExportPreview, ElementName=saveFilePopup}"
+                                                        Stretch="Uniform" HorizontalAlignment="Center" VerticalAlignment="Center"
+                                                        RenderOptions.BitmapInterpolationMode="None">
+                                    <visuals:SurfaceControl.Background>
+                                        <ImageBrush Source="/Images/CheckerTile.png" 
+                                                    TileMode="Tile" DestinationRect="0, 0, 25, 25"/>
+                                    </visuals:SurfaceControl.Background>
+                                </visuals:SurfaceControl>
+                            </Border>
+                        </Border>
+                    </Grid>
+                </Grid>
+            </Border>
+        </StackPanel>
     </DockPanel>
     </DockPanel>
-</dialogs:PixiEditorPopup>
+</dialogs:PixiEditorPopup>

+ 45 - 1
src/PixiEditor.AvaloniaUI/Views/Dialogs/ExportFilePopup.axaml.cs

@@ -1,10 +1,13 @@
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Avalonia;
 using Avalonia;
 using Avalonia.Platform.Storage;
 using Avalonia.Platform.Storage;
+using ChunkyImageLib;
 using CommunityToolkit.Mvvm.Input;
 using CommunityToolkit.Mvvm.Input;
 using PixiEditor.AvaloniaUI.Helpers;
 using PixiEditor.AvaloniaUI.Helpers;
 using PixiEditor.AvaloniaUI.Models.Files;
 using PixiEditor.AvaloniaUI.Models.Files;
+using PixiEditor.AvaloniaUI.ViewModels.Document;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.Numerics;
 
 
 namespace PixiEditor.AvaloniaUI.Views.Dialogs;
 namespace PixiEditor.AvaloniaUI.Views.Dialogs;
 
 
@@ -57,6 +60,15 @@ internal partial class ExportFilePopup : PixiEditorPopup
     public static readonly StyledProperty<string> SuggestedNameProperty = AvaloniaProperty.Register<ExportFilePopup, string>(
     public static readonly StyledProperty<string> SuggestedNameProperty = AvaloniaProperty.Register<ExportFilePopup, string>(
         nameof(SuggestedName));
         nameof(SuggestedName));
 
 
+    public static readonly StyledProperty<Surface> ExportPreviewProperty = AvaloniaProperty.Register<ExportFilePopup, Surface>(
+        nameof(ExportPreview));
+
+    public Surface ExportPreview
+    {
+        get => GetValue(ExportPreviewProperty);
+        set => SetValue(ExportPreviewProperty, value);
+    }
+
     public string SuggestedName
     public string SuggestedName
     {
     {
         get => GetValue(SuggestedNameProperty);
         get => GetValue(SuggestedNameProperty);
@@ -76,8 +88,16 @@ internal partial class ExportFilePopup : PixiEditorPopup
     }
     }
 
 
     public string SizeHint => new LocalizedString("EXPORT_SIZE_HINT", GetBestPercentage());
     public string SizeHint => new LocalizedString("EXPORT_SIZE_HINT", GetBestPercentage());
+    
+    private DocumentViewModel document;
 
 
-    public ExportFilePopup(int imageWidth, int imageHeight)
+    static ExportFilePopup()
+    {
+        SaveWidthProperty.Changed.Subscribe(RerenderPreview);
+        SaveHeightProperty.Changed.Subscribe(RerenderPreview);
+    }
+
+    public ExportFilePopup(int imageWidth, int imageHeight, DocumentViewModel document)
     {
     {
         SaveWidth = imageWidth;
         SaveWidth = imageWidth;
         SaveHeight = imageHeight;
         SaveHeight = imageHeight;
@@ -91,6 +111,22 @@ internal partial class ExportFilePopup : PixiEditorPopup
 
 
         SetBestPercentageCommand = new RelayCommand(SetBestPercentage);
         SetBestPercentageCommand = new RelayCommand(SetBestPercentage);
         ExportCommand = new AsyncRelayCommand(Export);
         ExportCommand = new AsyncRelayCommand(Export);
+        this.document = document;
+        RenderPreview();
+    }
+    
+    private void RenderPreview()
+    {
+        if (document == null)
+        {
+            return;
+        }
+        
+        var rendered = document.TryRenderWholeImage();
+        if (rendered.IsT1)
+        {
+            ExportPreview = rendered.AsT1.ResizeNearestNeighbor(new VecI(SaveWidth, SaveHeight));
+        }
     }
     }
 
 
     private async Task Export()
     private async Task Export()
@@ -153,4 +189,12 @@ internal partial class ExportFilePopup : PixiEditorPopup
         sizePicker.PercentageRb.IsChecked = true;
         sizePicker.PercentageRb.IsChecked = true;
         sizePicker.PercentageLostFocus();
         sizePicker.PercentageLostFocus();
     }
     }
+    
+    private static void RerenderPreview(AvaloniaPropertyChangedEventArgs e)
+    {
+        if (e.Sender is ExportFilePopup popup)
+        {
+            popup.RenderPreview();
+        }
+    }
 }
 }

+ 14 - 0
src/PixiEditor.AvaloniaUI/Views/Visuals/SurfaceControl.cs

@@ -21,6 +21,15 @@ internal class SurfaceControl : Control
     public static readonly StyledProperty<Stretch> StretchProperty = AvaloniaProperty.Register<SurfaceControl, Stretch>(
     public static readonly StyledProperty<Stretch> StretchProperty = AvaloniaProperty.Register<SurfaceControl, Stretch>(
         nameof(Stretch), Stretch.Uniform);
         nameof(Stretch), Stretch.Uniform);
 
 
+    public static readonly StyledProperty<IBrush> BackgroundProperty = AvaloniaProperty.Register<SurfaceControl, IBrush>(
+        nameof(Background));
+
+    public IBrush Background
+    {
+        get => GetValue(BackgroundProperty);
+        set => SetValue(BackgroundProperty, value);
+    }
+
     public Stretch Stretch
     public Stretch Stretch
     {
     {
         get => GetValue(StretchProperty);
         get => GetValue(StretchProperty);
@@ -88,6 +97,11 @@ internal class SurfaceControl : Control
         {
         {
             return;
             return;
         }
         }
+        
+        if (Background != null)
+        {
+            context.FillRectangle(Background, new Rect(0,0, Bounds.Width, Bounds.Height));
+        }
 
 
         var bounds = new Rect(Bounds.Size);
         var bounds = new Rect(Bounds.Size);
         var operation = new DrawSurfaceOperation(bounds, Surface, Stretch, Opacity);
         var operation = new DrawSurfaceOperation(bounds, Surface, Stretch, Opacity);