diff --git a/src/Models/ImageDecoder.cs b/src/Models/ImageDecoder.cs
index ce3a44c1..6fe0f428 100644
--- a/src/Models/ImageDecoder.cs
+++ b/src/Models/ImageDecoder.cs
@@ -4,6 +4,7 @@
{
None = 0,
Builtin,
- Pfim
+ Pfim,
+ Tiff,
}
}
diff --git a/src/SourceGit.csproj b/src/SourceGit.csproj
index 62ec6255..5205ae4f 100644
--- a/src/SourceGit.csproj
+++ b/src/SourceGit.csproj
@@ -47,6 +47,7 @@
+
diff --git a/src/ViewModels/ImageSource.cs b/src/ViewModels/ImageSource.cs
index cd81a1f8..f94b0c95 100644
--- a/src/ViewModels/ImageSource.cs
+++ b/src/ViewModels/ImageSource.cs
@@ -7,6 +7,7 @@ using Avalonia;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
+using BitMiracle.LibTiff.Classic;
using Pfim;
namespace SourceGit.ViewModels
@@ -39,6 +40,9 @@ namespace SourceGit.ViewModels
case ".tga":
case ".dds":
return Models.ImageDecoder.Pfim;
+ case ".tif":
+ case ".tiff":
+ return Models.ImageDecoder.Tiff;
default:
return Models.ImageDecoder.None;
}
@@ -70,10 +74,22 @@ namespace SourceGit.ViewModels
var size = stream.Length;
if (size > 0)
{
- if (decoder == Models.ImageDecoder.Builtin)
- return DecodeWithAvalonia(stream, size);
- else if (decoder == Models.ImageDecoder.Pfim)
- return DecodeWithPfim(stream, size);
+ try
+ {
+ switch (decoder)
+ {
+ case Models.ImageDecoder.Builtin:
+ return DecodeWithAvalonia(stream, size);
+ case Models.ImageDecoder.Pfim:
+ return DecodeWithPfim(stream, size);
+ case Models.ImageDecoder.Tiff:
+ return DecodeWithTiff(stream, size);
+ }
+ }
+ catch (Exception e)
+ {
+ Console.Out.WriteLine(e.Message);
+ }
}
return new ImageSource(null, 0);
@@ -81,75 +97,83 @@ namespace SourceGit.ViewModels
private static ImageSource DecodeWithAvalonia(Stream stream, long size)
{
- try
- {
- var bitmap = new Bitmap(stream);
- return new ImageSource(bitmap, size);
- }
- catch
- {
- return new ImageSource(null, 0);
- }
+ var bitmap = new Bitmap(stream);
+ return new ImageSource(bitmap, size);
}
private static ImageSource DecodeWithPfim(Stream stream, long size)
{
- try
+ using (var pfiImage = Pfimage.FromStream(stream))
{
- using (var pfiImage = Pfimage.FromStream(stream))
+ var data = pfiImage.Data;
+ var stride = pfiImage.Stride;
+
+ var pixelFormat = PixelFormats.Bgra8888;
+ var alphaFormat = AlphaFormat.Opaque;
+ switch (pfiImage.Format)
{
- var data = pfiImage.Data;
- var stride = pfiImage.Stride;
+ case ImageFormat.Rgb8:
+ pixelFormat = PixelFormats.Gray8;
+ break;
+ case ImageFormat.R5g5b5:
+ case ImageFormat.R5g5b5a1:
+ pixelFormat = PixelFormats.Bgr555;
+ break;
+ case ImageFormat.R5g6b5:
+ pixelFormat = PixelFormats.Bgr565;
+ break;
+ case ImageFormat.Rgb24:
+ pixelFormat = PixelFormats.Bgr24;
+ break;
+ case ImageFormat.Rgba16:
+ var pixels2 = pfiImage.DataLen / 2;
+ data = new byte[pixels2 * 4];
+ stride = pfiImage.Width * 4;
+ for (var i = 0; i < pixels2; i++)
+ {
+ var src = BitConverter.ToUInt16(pfiImage.Data, i * 2);
+ data[i * 4 + 0] = (byte)Math.Round((src & 0x0F) / 15F * 255); // B
+ data[i * 4 + 1] = (byte)Math.Round(((src >> 4) & 0x0F) / 15F * 255); // G
+ data[i * 4 + 2] = (byte)Math.Round(((src >> 8) & 0x0F) / 15F * 255); // R
+ data[i * 4 + 3] = (byte)Math.Round(((src >> 12) & 0x0F) / 15F * 255); // A
+ }
- var pixelFormat = PixelFormats.Bgra8888;
- var alphaFormat = AlphaFormat.Opaque;
- switch (pfiImage.Format)
- {
- case ImageFormat.Rgb8:
- pixelFormat = PixelFormats.Gray8;
- break;
- case ImageFormat.R5g5b5:
- case ImageFormat.R5g5b5a1:
- pixelFormat = PixelFormats.Bgr555;
- break;
- case ImageFormat.R5g6b5:
- pixelFormat = PixelFormats.Bgr565;
- break;
- case ImageFormat.Rgb24:
- pixelFormat = PixelFormats.Bgr24;
- break;
- case ImageFormat.Rgba16:
- var pixels2 = pfiImage.DataLen / 2;
- data = new byte[pixels2 * 4];
- stride = pfiImage.Width * 4;
- for (var i = 0; i < pixels2; i++)
- {
- var src = BitConverter.ToUInt16(pfiImage.Data, i * 2);
- data[i * 4 + 0] = (byte)Math.Round((src & 0x0F) / 15F * 255); // B
- data[i * 4 + 1] = (byte)Math.Round(((src >> 4) & 0x0F) / 15F * 255); // G
- data[i * 4 + 2] = (byte)Math.Round(((src >> 8) & 0x0F) / 15F * 255); // R
- data[i * 4 + 3] = (byte)Math.Round(((src >> 12) & 0x0F) / 15F * 255); // A
- }
-
- alphaFormat = AlphaFormat.Premul;
- break;
- case ImageFormat.Rgba32:
- alphaFormat = AlphaFormat.Premul;
- break;
- default:
- return new ImageSource(null, 0);
- }
-
- var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(data, 0);
- var pixelSize = new PixelSize(pfiImage.Width, pfiImage.Height);
- var dpi = new Vector(96, 96);
- var bitmap = new Bitmap(pixelFormat, alphaFormat, ptr, pixelSize, dpi, stride);
- return new ImageSource(bitmap, size);
+ alphaFormat = AlphaFormat.Premul;
+ break;
+ case ImageFormat.Rgba32:
+ alphaFormat = AlphaFormat.Premul;
+ break;
+ default:
+ return new ImageSource(null, 0);
}
+
+ var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(data, 0);
+ var pixelSize = new PixelSize(pfiImage.Width, pfiImage.Height);
+ var dpi = new Vector(96, 96);
+ var bitmap = new Bitmap(pixelFormat, alphaFormat, ptr, pixelSize, dpi, stride);
+ return new ImageSource(bitmap, size);
}
- catch
+ }
+
+ private static ImageSource DecodeWithTiff(Stream stream, long size)
+ {
+ using (var tiff = Tiff.ClientOpen($"{Guid.NewGuid()}.tif", "r", stream, new TiffStream()))
{
- return new ImageSource(null, 0);
+ if (tiff == null)
+ return new ImageSource(null, 0);
+
+ var width = tiff.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
+ var height = tiff.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
+ var pixels = new int[width * height];
+
+ // Currently only supports image when its `BITSPERSAMPLE` is one in [1,2,4,8,16]
+ tiff.ReadRGBAImageOriented(width, height, pixels, Orientation.TOPLEFT);
+
+ var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(pixels, 0);
+ var pixelSize = new PixelSize(width, height);
+ var dpi = new Vector(96, 96);
+ var bitmap = new Bitmap(PixelFormats.Rgba8888, AlphaFormat.Premul, ptr, pixelSize, dpi, width * 4);
+ return new ImageSource(bitmap, size);
}
}
}