Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
77 changes: 75 additions & 2 deletions Svg.Editor.Avalonia.Forms/Services/FormsPickImageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,79 @@ public FormsPickImageService()
_mainWindow= MainWindow;
}

public async Task<string> PickZipFilePathAsync()
{
var options = new FilePickerOpenOptions
{
Title = "Select Tile Zip",
AllowMultiple = false,
FileTypeFilter = new[]
{
new FilePickerFileType("Zip Files")
{
Patterns = new[] { "*.zip" },
MimeTypes = new[] { "application/zip" }
}
}
};

var result = await _mainWindow.StorageProvider.OpenFilePickerAsync(options);
if (result == null || result.Count == 0)
return null;

return result[0].Path.LocalPath;
}

public async Task<string> PickImageOrZipPathAsync(int maxPixelDimension)
{
var options = new FilePickerOpenOptions
{
Title = "Select Image or Tile Zip",
AllowMultiple = false,
FileTypeFilter = new[]
{
new FilePickerFileType("Images & Zips")
{
Patterns = new[] { "*.png", "*.jpg", "*.jpeg", "*.svg", "*.zip" },
MimeTypes = new[] { "image/*", "application/zip" }
}
}
};

var result = await _mainWindow.StorageProvider.OpenFilePickerAsync(options);
if (result == null || result.Count == 0)
return null;

var selectedFile = result[0];
var name = selectedFile.Name;

if (name.EndsWith(".zip", System.StringComparison.OrdinalIgnoreCase))
return selectedFile.Path.LocalPath;

try
{
var fs = SvgEngine.Resolve<IFileSystem>();
var path = name.EndsWith(".svg", System.StringComparison.OrdinalIgnoreCase) ? "background.svg" : "background.png";
var fullPath = fs.PathCombine(fs.GetDefaultStoragePath(), path);

if (fs.FileExists(fullPath))
fs.DeleteFile(fullPath);

using (var inStream = await selectedFile.OpenReadAsync())
using (var outStream = fs.OpenWrite(fullPath))
{
await inStream.CopyToAsync(outStream);
}

return path;
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error picking file: {ex.Message}");
return null;
}
}

public async Task<string> PickImagePathAsync(int maxPixelDimension)
{
var filePickerOptions = new FilePickerOpenOptions
Expand All @@ -26,7 +99,7 @@ public async Task<string> PickImagePathAsync(int maxPixelDimension)
{
new FilePickerFileType("Images")
{
Patterns = new[] { "*.png", "*.jpg", "*.jpeg", "*.bmp", "*.gif" },
Patterns = new[] { "*.png", "*.jpg", "*.jpeg", "*.bmp", "*.gif", "*.svg" },
MimeTypes = new[] { "image/*" }
}
}
Expand All @@ -42,7 +115,7 @@ public async Task<string> PickImagePathAsync(int maxPixelDimension)
try
{
var fs = SvgEngine.Resolve<IFileSystem>();
var path = "background.png";
var path = selectedFile.Name.EndsWith(".svg")? "background.svg" : "background.png";
var fullPath = fs.PathCombine(fs.GetDefaultStoragePath(), path);

if (fs.FileExists(fullPath))
Expand Down
4 changes: 3 additions & 1 deletion Svg.Editor.Avalonia.Forms/Svg.Editor.Avalon.Forms.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<Version>3.1.3-optiq01</Version>
<Version>3.1.4-optiq01</Version>
<PackageReleaseNotes>
#3.1.4-optiq01
Added filetiling for large images and svg files
#3.1.3-optiq01
Updated SkiaSharp from 2.88.8 to 3.119.1
#3.1.2-optiq06
Expand Down
26 changes: 21 additions & 5 deletions Svg.Editor.Avalonia.Views/SKCanvasView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class SKCanvasView : Decorator

private static readonly Vector Dpi = new Vector(96, 96);
private WriteableBitmap? _writeableBitmap = default;
private bool _IgnorePixelScaling;
private bool _IgnorePixelScaling = true;
private int _pixelWidth;
private int _pixelHeight;
private double _scale = 1;
Expand Down Expand Up @@ -109,8 +109,16 @@ private void RepaintSurface()
return;
}

var bitmap = _writeableBitmap ??= new WriteableBitmap(new PixelSize(_pixelWidth, _pixelHeight), Dpi,
Avalonia.Platform.PixelFormat.Bgra8888, AlphaFormat.Premul);
WriteableBitmap? old = null;
if (_writeableBitmap is null ||
_writeableBitmap.PixelSize.Width != _pixelWidth ||
_writeableBitmap.PixelSize.Height != _pixelHeight)
{
old = _writeableBitmap;
_writeableBitmap = new WriteableBitmap(new PixelSize(_pixelWidth, _pixelHeight), Dpi,
Avalonia.Platform.PixelFormat.Bgra8888, AlphaFormat.Premul);
}
var bitmap = _writeableBitmap;
var scale = this.Scale;
using (var framebuffer = bitmap.Lock())
{
Expand Down Expand Up @@ -142,6 +150,14 @@ private void RepaintSurface()
{
AlignmentX = AlignmentX.Left, AlignmentY = AlignmentY.Top, Stretch = Stretch.Fill
}.ToImmutable());

if (old is not null)
{
// Defer disposal so the render thread has time to pick up the new ImageBrush
// before the old WriteableBitmap's native memory is freed. Background priority
// runs after Render, guaranteeing the new composition snapshot is published first.
Dispatcher.UIThread.Post(old.Dispose, DispatcherPriority.Background);
}
return;
}

Expand Down Expand Up @@ -175,6 +191,8 @@ protected override void OnLoaded(RoutedEventArgs e)
protected override void OnUnloaded(RoutedEventArgs e)
{
base.OnUnloaded(e);
_writeableBitmap?.Dispose();
_writeableBitmap = null;
/*
var display = DisplayInformation.GetForCurrentView();
display.DpiChanged -= OnDpiChanged;
Expand Down Expand Up @@ -236,8 +254,6 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
_pixelWidth = Convert.ToInt32(bounds.Width * scale);
_pixelHeight = Convert.ToInt32(bounds.Height * scale);
this.CanvasSize = new Size(_pixelWidth, _pixelHeight);
_writeableBitmap?.Dispose();
_writeableBitmap = null;
this.InvalidateSurface();
}

Expand Down
4 changes: 3 additions & 1 deletion Svg.Editor.Avalonia.Views/Svg.Editor.Avalon.Views.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<Version>3.1.3-optiq01</Version>
<Version>3.1.4-optiq01</Version>
<PackageReleaseNotes>
#3.1.4-optiq01
Added filetiling for large images and svg files
#3.1.3-optiq01
Updated SkiaSharp from 2.88.8 to 3.119.1
#3.1.2-optiq06
Expand Down
10 changes: 10 additions & 0 deletions Svg.Editor.Core/Interfaces/IPickImageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,15 @@ namespace Svg.Editor.Interfaces
public interface IPickImageService
{
Task<string> PickImagePathAsync(int maxPixelDimension);

/// <summary>Returns the absolute path of a user-selected zip file, or null if cancelled.</summary>
Task<string> PickZipFilePathAsync();

/// <summary>
/// Opens a single file picker that accepts images (png/jpg/svg) and zip files.
/// Returns the path to the selected file, or null if cancelled.
/// The returned path is absolute for zip files and relative-to-storage for images.
/// </summary>
Task<string> PickImageOrZipPathAsync(int maxPixelDimension);
}
}
4 changes: 3 additions & 1 deletion Svg.Editor.Core/Svg.Editor.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
<AssemblyName>Svg.Editor</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<TargetFramework>netstandard2.0</TargetFramework>
<Version>3.1.3-optiq01</Version>
<Version>3.1.4-optiq01</Version>
<LangVersion>latest</LangVersion>
<PackageReleaseNotes>
#3.1.4-optiq01
Added filetiling for large images and svg files
#3.1.3-optiq01
Updated SkiaSharp from 2.88.8 to 3.119.1
#3.1.2-optiq06
Expand Down
Loading
Loading