diff --git a/src/Models/ImageDecoder.cs b/src/Models/ImageDecoder.cs index 3a758882..ce3a44c1 100644 --- a/src/Models/ImageDecoder.cs +++ b/src/Models/ImageDecoder.cs @@ -4,5 +4,6 @@ { None = 0, Builtin, + Pfim } } diff --git a/src/SourceGit.csproj b/src/SourceGit.csproj index 3fe21b1a..62ec6255 100644 --- a/src/SourceGit.csproj +++ b/src/SourceGit.csproj @@ -50,6 +50,7 @@ + diff --git a/src/ViewModels/ImageSource.cs b/src/ViewModels/ImageSource.cs index ec6b6a8d..2be09809 100644 --- a/src/ViewModels/ImageSource.cs +++ b/src/ViewModels/ImageSource.cs @@ -1,5 +1,7 @@ using System.IO; +using System.Runtime.InteropServices; using Avalonia.Media.Imaging; +using Pfim; namespace SourceGit.ViewModels { @@ -27,6 +29,9 @@ namespace SourceGit.ViewModels case ".png": case ".webp": return Models.ImageDecoder.Builtin; + case ".tga": + case ".dds": + return Models.ImageDecoder.Pfim; default: return Models.ImageDecoder.None; } @@ -70,9 +75,93 @@ namespace SourceGit.ViewModels // Just ignore. } } + else if (decoder == Models.ImageDecoder.Pfim) + { + return new ImageSource(LoadWithPfim(stream), size); + } } return new ImageSource(null, 0); } + + private static Bitmap LoadWithPfim(Stream stream) + { + var image = Pfim.Pfimage.FromStream(stream); + byte[] data; + int stride; + if (image.Format == ImageFormat.Rgba32) + { + data = image.Data; + stride = image.Stride; + } + else + { + int pixels = image.Width * image.Height; + data = new byte[pixels * 4]; + stride = image.Width * 4; + + switch (image.Format) + { + case ImageFormat.Rgba16: + case ImageFormat.R5g5b5a1: + { + for (int i = 0; i < pixels; i++) + { + data[i * 4 + 0] = image.Data[i * 4 + 2]; // B + data[i * 4 + 1] = image.Data[i * 4 + 1]; // G + data[i * 4 + 2] = image.Data[i * 4 + 0]; // R + data[i * 4 + 3] = image.Data[i * 4 + 3]; // A + } + } + break; + case ImageFormat.R5g5b5: + case ImageFormat.R5g6b5: + case ImageFormat.Rgb24: + { + for (int i = 0; i < pixels; i++) + { + data[i * 4 + 0] = image.Data[i * 3 + 2]; // B + data[i * 4 + 1] = image.Data[i * 3 + 1]; // G + data[i * 4 + 2] = image.Data[i * 3 + 0]; // R + data[i * 4 + 3] = 255; // A + } + } + break; + case ImageFormat.Rgb8: + { + for (int i = 0; i < pixels; i++) + { + var color = image.Data[i]; + data[i * 4 + 0] = color; + data[i * 4 + 1] = color; + data[i * 4 + 2] = color; + data[i * 4 + 3] = 255; + } + } + break; + default: + return null; + } + } + + // Pin the array and pass the pointer to Bitmap + var handle = GCHandle.Alloc(data, GCHandleType.Pinned); + try + { + var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(data, 0); + var bitmap = new Bitmap( + Avalonia.Platform.PixelFormat.Bgra8888, + Avalonia.Platform.AlphaFormat.Unpremul, + ptr, + new Avalonia.PixelSize(image.Width, image.Height), + new Avalonia.Vector(96, 96), + stride); + return bitmap; + } + finally + { + handle.Free(); + } + } } }