Skip to content

Commit

Permalink
fix: Using SkiaSharp instead of SixLabors.ImageSharp
Browse files Browse the repository at this point in the history
  • Loading branch information
QuentinCraft committed Dec 10, 2024
1 parent 5132654 commit 11f4aaa
Show file tree
Hide file tree
Showing 11 changed files with 207 additions and 106 deletions.
149 changes: 115 additions & 34 deletions Luxoria.App/Luxoria.App/EventHandlers/CollectionUpdatedHandler.cs
Original file line number Diff line number Diff line change
@@ -1,46 +1,127 @@
using Luxoria.Modules.Models.Events;
using Luxoria.App.EventHandlers;
using Luxoria.Modules.Models;
using Luxoria.Modules.Models.Events;
using Luxoria.Modules.Utils;
using Luxoria.SDK.Interfaces;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Imaging;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Windows.UI.Notifications;
using System.Threading.Tasks;
using Windows.Graphics.Imaging;
using Windows.Storage.Streams;

namespace Luxoria.App.EventHandlers;

/// <summary>
/// Handles the CollectionUpdatedEvent.
/// </summary>
public class CollectionUpdatedHandler
namespace Luxoria.App.EventHandlers
{
/// <summary>
/// The logger service.
/// </summary>
private readonly ILoggerService _loggerService;

/// <summary>
/// Constructor for the CollectionUpdatedHandler.
/// </summary>
/// <param name="loggerService"></param>
public CollectionUpdatedHandler(ILoggerService loggerService)
public class CollectionUpdatedHandler
{
_loggerService = loggerService;
}
private readonly ILoggerService _loggerService;

/// <summary>
/// Handles the CollectionUpdatedEvent.
/// </summary>
public void OnCollectionUpdated(CollectionUpdatedEvent body)
{
_loggerService.Log($"Collection updated: {body.CollectionName}");
_loggerService.Log($"Collection path: {body.CollectionPath}");
public CollectionUpdatedHandler(ILoggerService loggerService)
{
_loggerService = loggerService;
}

// Handles the CollectionUpdatedEvent
public async Task OnCollectionUpdated(CollectionUpdatedEvent body, Image imageControl)
{
_loggerService.Log($"Collection updated: {body.CollectionName}");
_loggerService.Log($"Collection path: {body.CollectionPath}");

var toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01);
var textNodes = toastXml.GetElementsByTagName("text");
textNodes[0].AppendChild(toastXml.CreateTextNode($"Updated Collection: {body.CollectionName}"));
var toast = new ToastNotification(toastXml);
ToastNotificationManager.CreateToastNotifier("Luxoria").Show(toast);
// Iterate over the assets and process each image
for (int i = 0; i < body.Assets.Count; i++)
{
var asset = body.Assets.ElementAt(i);
_loggerService.Log($"Asset {i}: {asset.MetaData.Id}");

// Get the pixel data from the asset
ReadOnlyMemory<byte> bitmap = asset.Data.PixelData;

// Show basic info about the bitmap for debugging
Debug.WriteLine($"Bitmap data length: {bitmap.Length} bytes");
Debug.WriteLine($"Bitmap width: {asset.Data.Width}");
Debug.WriteLine($"Bitmap height: {asset.Data.Height}");

try
{
// Convert the byte array to SoftwareBitmap
var softwareBitmap = ConvertToSoftwareBitmap(bitmap, asset.Data.Width, asset.Data.Height);

// Convert SoftwareBitmap to BitmapImage (ImageSource compatible)
BitmapImage bitmapImage = await ConvertToBitmapImage(softwareBitmap);

// Set the BitmapImage as the source of the Image control
imageControl.Source = bitmapImage;
}
catch (Exception ex)
{
// Log the exception details for debugging
_loggerService.Log($"Error processing asset {asset.MetaData.Id}: {ex.Message}");
Debug.WriteLine($"Error processing asset {asset.MetaData.Id}: {ex.StackTrace}");
}
}
}

for (int i = 0; i < body.Assets.Count; i++)
// Convert byte array (ReadOnlyMemory<byte>) to SoftwareBitmap
private SoftwareBitmap ConvertToSoftwareBitmap(ReadOnlyMemory<byte> bitmap, int width, int height)
{
_loggerService.Log($"Asset {i}: {body.Assets.ElementAt(i).MetaData.Id}");
try
{
// Log first few bytes of the bitmap to verify if it contains valid image data (e.g., PNG or JPEG header)
string bytePreview = BitConverter.ToString(bitmap.Span.Slice(0, 10).ToArray());
Debug.WriteLine($"First 10 bytes of image data: {bytePreview}");

// Create a stream to hold the pixel data
var stream = new InMemoryRandomAccessStream();
var writer = new DataWriter(stream.GetOutputStreamAt(0));

// Write the byte data to the stream
writer.WriteBytes(bitmap.Span.ToArray());
writer.StoreAsync().AsTask().Wait();

// Seek back to the beginning of the stream
stream.Seek(0);

// Create a BitmapDecoder to decode the byte data
var decoder = BitmapDecoder.CreateAsync(stream).AsTask().Result;

// Retrieve the SoftwareBitmap from the decoder
return decoder.GetSoftwareBitmapAsync().AsTask().Result;
}
catch (Exception ex)
{
Debug.WriteLine($"Error in ConvertToSoftwareBitmap: {ex.Message}");
throw new InvalidOperationException("Failed to convert byte array to SoftwareBitmap.", ex);
}
}


// Convert SoftwareBitmap to BitmapImage (which is an ImageSource)
private async Task<BitmapImage> ConvertToBitmapImage(SoftwareBitmap softwareBitmap)
{
try
{
var bitmapImage = new BitmapImage();
var stream = new InMemoryRandomAccessStream();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);

// Encode the SoftwareBitmap into the stream
encoder.SetSoftwareBitmap(softwareBitmap);
await encoder.FlushAsync();

// Set the stream as the source for the BitmapImage
stream.Seek(0);
await bitmapImage.SetSourceAsync(stream);

return bitmapImage;
}
catch (Exception ex)
{
Debug.WriteLine($"Error in ConvertToBitmapImage: {ex.Message}");
throw new InvalidOperationException("Failed to convert SoftwareBitmap to BitmapImage.", ex);
}
}
}
}
3 changes: 3 additions & 0 deletions Luxoria.App/Luxoria.App/Luxoria.App.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.241114003" />
<PackageReference Include="SkiaSharp" Version="3.116.1" />
<PackageReference Include="SkiaSharp.Views.WinUI" Version="3.116.1" />
<PackageReference Include="System.Drawing.Common" Version="9.0.0" />
<Manifest Include="$(ApplicationManifest)" />
</ItemGroup>

Expand Down
8 changes: 8 additions & 0 deletions Luxoria.App/Luxoria.App/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@
Margin="0,0,0,20"
Height="30"
Grid.Row="3"/>

<Image
x:Name="Image"
Grid.Row="4"
Stretch="Uniform"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0,20,0,0"/>
</Grid>
</Grid>
</Window>
7 changes: 6 additions & 1 deletion Luxoria.App/Luxoria.App/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ public MainWindow(IEventBus eventBus, ILoggerService loggerService)
private void InitializeEventBus()
{
// Subscribe to events that will be published through the event bus
_eventBus.Subscribe<CollectionUpdatedEvent>(_collectionUpdatedHandler.OnCollectionUpdated);
_eventBus.Subscribe<CollectionUpdatedEvent>(async (body) =>
{
// Assuming 'imageControl' is your Image UI control you want to update
// Pass 'imageControl' to the handler
await _collectionUpdatedHandler.OnCollectionUpdated(body, Image);
});
}

/// <summary>
Expand Down
4 changes: 4 additions & 0 deletions Luxoria.App/Luxoria.Modules/Luxoria.Modules.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
<Folder Include="Services\" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="SkiaSharp" Version="3.116.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Luxoria.SDK\Luxoria.SDK.csproj" />
</ItemGroup>
Expand Down
8 changes: 7 additions & 1 deletion Luxoria.App/Luxoria.Modules/Models/ImageData.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Luxoria.Modules.Models;
using SkiaSharp;

namespace Luxoria.Modules.Models;

/// <summary>
/// Represents an image with pixel data, dimensions, and format information.
Expand All @@ -25,6 +27,10 @@ public class ImageData
/// </summary>
public FileExtension Format { get; }

/// <summary>
/// </summary>
public SKImage Image { get; }

/// <summary>
/// Initializes a new instance of the <see cref="ImageData"/> class.
/// </summary>
Expand Down
4 changes: 4 additions & 0 deletions Modules/LuxImport/LuxImport/LuxImport.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.UI.Xaml" Version="2.8.6" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.6" />
<PackageReference Include="SkiaSharp" Version="3.116.1" />
<PackageReference Include="SkiaSharp.NativeAssets.macOS" Version="3.116.1" />
<PackageReference Include="SkiaSharp.NativeAssets.Win32" Version="3.116.1" />
</ItemGroup>

<ItemGroup>
Expand Down
130 changes: 60 additions & 70 deletions Modules/LuxImport/LuxImport/Utils/ImageDataHelper.cs
Original file line number Diff line number Diff line change
@@ -1,89 +1,79 @@
using Luxoria.Modules.Models;
using Luxoria.Modules.Utils;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SkiaSharp;
using System;
using System.IO;
using System.Diagnostics;
using Luxoria.Modules.Models;

namespace LuxImport.Utils;

public static class ImageDataHelper
namespace LuxImport.Utils
{
/// <summary>
/// Load image data from a specified path
/// </summary>
/// <param name="path">The path to the image file</param>
/// <returns>An ImageData object containing the loaded image's data</returns>
public static ImageData LoadFromPath(string path)
public static class ImageDataHelper
{
// Check if the path is valid
if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentException("Path cannot be null or empty.", nameof(path));
}

if (!File.Exists(path))
{
throw new FileNotFoundException("The specified file does not exist.", path);
}

// Extract the file extension and validate it
string extension = Path.GetExtension(path);
FileExtension ext = FileExtensionHelper.ConvertToEnum(extension);
if (ext == FileExtension.UNKNOWN)
{
throw new NotSupportedException($"File format '{extension}' is not supported.");
}

try
/// <summary>
/// Load image data from a specified path and convert it into a byte array.
/// </summary>
/// <param name="path">The path to the image file.</param>
/// <returns>An ImageData object containing the loaded image's data.</returns>
public static ImageData LoadFromPath(string path)
{
// Log the start of the image loading process
Debug.WriteLine($"Attempting to load image from path: {path}");

// Read the file bytes
byte[] fileBytes = File.ReadAllBytes(path);

// Log file size for debugging purposes
Debug.WriteLine($"Loaded {fileBytes.Length} bytes from {path}");
// Check if the path is valid
if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentException("Path cannot be null or empty.", nameof(path));
}

if (fileBytes.Length == 0)
if (!File.Exists(path))
{
throw new InvalidOperationException($"The file at '{path}' is empty.");
throw new FileNotFoundException("The specified file does not exist.", path);
}

// Load the image using ImageSharp
using (MemoryStream memoryStream = new MemoryStream(fileBytes))
try
{
using (Image<Rgba32> image = Image.Load<Rgba32>(memoryStream))
// Log the start of the image loading process
Debug.WriteLine($"Attempting to load image from path: {path}");

// Load the image using SkiaSharp
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
// Check if the image is valid
if (image == null)
// Decode the image from the stream
Debug.WriteLine("1/ Decoding the image using SkiaSharp...");
var skData = SKData.Create(stream);
Debug.WriteLine("1.1/ SKData created successfully.");
var skiaImage = SKBitmap.Decode(skData); // This will automatically detect the format
Debug.WriteLine("2/ Image decoded successfully.");
if (skiaImage == null)
{
throw new InvalidOperationException($"Failed to decode image at path: {path}");
throw new InvalidOperationException($"Failed to decode the image at '{path}'.");
}
Debug.WriteLine($"Image dimensions: {skiaImage.Width}x{skiaImage.Height}");
// Convert the image to a byte array (PNG format)
using (var memoryStream = new MemoryStream())
{
Debug.WriteLine("3/ Encoding the image as PNG...");
skiaImage.Encode(SKEncodedImageFormat.Png, 100).SaveTo(memoryStream);
Debug.WriteLine("4/ Image encoded successfully.");
Debug.WriteLine($"Image size: {memoryStream.Length} bytes");
byte[] imageBytes = memoryStream.ToArray();
Debug.WriteLine("5/ Image converted to byte array successfully.");

// Log successful decoding for debugging purposes
Debug.WriteLine($"Image decoded successfully. Width: {image.Width}, Height: {image.Height}");

// Directly access pixel data (in RGBA format) without extra encoding
byte[] imageBytes = new byte[image.Width * image.Height * 4]; // RGBA32 = 4 bytes per pixel
image.CopyPixelDataTo(imageBytes); // Copy the pixel data directly to the byte array

// Create and return an ImageData object
return new ImageData(
imageBytes,
image.Width,
image.Height,
ext
);
// Return an ImageData object with the necessary data
return new ImageData(
imageBytes,
skiaImage.Width,
skiaImage.Height,
FileExtension.PNG//,
//skiaImage // You can use SKBitmap for further manipulation or display
);
}
}
}
}
catch (Exception ex)
{
// Log detailed error message and stack trace for troubleshooting
Debug.WriteLine($"An error occurred while loading the image at '{path}': {ex.Message}");
Debug.WriteLine($"Stack Trace: {ex.StackTrace}");
throw new InvalidOperationException($"An error occurred while loading the image at '{path}': {ex.Message}", ex);
catch (Exception ex)
{
// Log detailed error message and stack trace for troubleshooting
Debug.WriteLine($"An error occurred while loading the image at '{path}': {ex.Message}");
Debug.WriteLine($"Stack Trace: {ex.StackTrace}");
throw new InvalidOperationException($"An error occurred while loading the image at '{path}': {ex.Message}", ex);
}
}
}
}

Binary file added assets/BaseCollection/NET Logo_resized.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes

0 comments on commit 11f4aaa

Please sign in to comment.