Skip to content

Commit 05757eb

Browse files
committed
feature: supports to view .tiff images
Signed-off-by: leo <longshuang@msn.cn>
1 parent cb6d6a2 commit 05757eb

File tree

3 files changed

+91
-65
lines changed

3 files changed

+91
-65
lines changed

src/Models/ImageDecoder.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ public enum ImageDecoder
44
{
55
None = 0,
66
Builtin,
7-
Pfim
7+
Pfim,
8+
Tiff,
89
}
910
}

src/SourceGit.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.2.0" />
4848
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.2.0" />
4949
<PackageReference Include="Azure.AI.OpenAI" Version="2.2.0-beta.4" />
50+
<PackageReference Include="BitMiracle.LibTiff.NET" Version="2.4.660" />
5051
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
5152
<PackageReference Include="LiveChartsCore.SkiaSharpView.Avalonia" Version="2.0.0-rc5.4" />
5253
<PackageReference Include="OpenAI" Version="2.2.0-beta.4" />

src/ViewModels/ImageSource.cs

Lines changed: 88 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Avalonia.Media.Imaging;
88
using Avalonia.Platform;
99

10+
using BitMiracle.LibTiff.Classic;
1011
using Pfim;
1112

1213
namespace SourceGit.ViewModels
@@ -39,6 +40,9 @@ public static Models.ImageDecoder GetDecoder(string file)
3940
case ".tga":
4041
case ".dds":
4142
return Models.ImageDecoder.Pfim;
43+
case ".tif":
44+
case ".tiff":
45+
return Models.ImageDecoder.Tiff;
4246
default:
4347
return Models.ImageDecoder.None;
4448
}
@@ -70,86 +74,106 @@ private static ImageSource LoadFromStream(Stream stream, Models.ImageDecoder dec
7074
var size = stream.Length;
7175
if (size > 0)
7276
{
73-
if (decoder == Models.ImageDecoder.Builtin)
74-
return DecodeWithAvalonia(stream, size);
75-
else if (decoder == Models.ImageDecoder.Pfim)
76-
return DecodeWithPfim(stream, size);
77+
try
78+
{
79+
switch (decoder)
80+
{
81+
case Models.ImageDecoder.Builtin:
82+
return DecodeWithAvalonia(stream, size);
83+
case Models.ImageDecoder.Pfim:
84+
return DecodeWithPfim(stream, size);
85+
case Models.ImageDecoder.Tiff:
86+
return DecodeWithTiff(stream, size);
87+
}
88+
}
89+
catch (Exception e)
90+
{
91+
Console.Out.WriteLine(e.Message);
92+
}
7793
}
7894

7995
return new ImageSource(null, 0);
8096
}
8197

8298
private static ImageSource DecodeWithAvalonia(Stream stream, long size)
8399
{
84-
try
85-
{
86-
var bitmap = new Bitmap(stream);
87-
return new ImageSource(bitmap, size);
88-
}
89-
catch
90-
{
91-
return new ImageSource(null, 0);
92-
}
100+
var bitmap = new Bitmap(stream);
101+
return new ImageSource(bitmap, size);
93102
}
94103

95104
private static ImageSource DecodeWithPfim(Stream stream, long size)
96105
{
97-
try
106+
using (var pfiImage = Pfimage.FromStream(stream))
98107
{
99-
using (var pfiImage = Pfimage.FromStream(stream))
100-
{
101-
var data = pfiImage.Data;
102-
var stride = pfiImage.Stride;
108+
var data = pfiImage.Data;
109+
var stride = pfiImage.Stride;
103110

104-
var pixelFormat = PixelFormats.Bgra8888;
105-
var alphaFormat = AlphaFormat.Opaque;
106-
switch (pfiImage.Format)
107-
{
108-
case ImageFormat.Rgb8:
109-
pixelFormat = PixelFormats.Gray8;
110-
break;
111-
case ImageFormat.R5g5b5:
112-
case ImageFormat.R5g5b5a1:
113-
pixelFormat = PixelFormats.Bgr555;
114-
break;
115-
case ImageFormat.R5g6b5:
116-
pixelFormat = PixelFormats.Bgr565;
117-
break;
118-
case ImageFormat.Rgb24:
119-
pixelFormat = PixelFormats.Bgr24;
120-
break;
121-
case ImageFormat.Rgba16:
122-
var pixels2 = pfiImage.DataLen / 2;
123-
data = new byte[pixels2 * 4];
124-
stride = pfiImage.Width * 4;
125-
for (var i = 0; i < pixels2; i++)
126-
{
127-
var src = BitConverter.ToUInt16(pfiImage.Data, i * 2);
128-
data[i * 4 + 0] = (byte)Math.Round((src & 0x0F) / 15F * 255); // B
129-
data[i * 4 + 1] = (byte)Math.Round(((src >> 4) & 0x0F) / 15F * 255); // G
130-
data[i * 4 + 2] = (byte)Math.Round(((src >> 8) & 0x0F) / 15F * 255); // R
131-
data[i * 4 + 3] = (byte)Math.Round(((src >> 12) & 0x0F) / 15F * 255); // A
132-
}
133-
134-
alphaFormat = AlphaFormat.Premul;
135-
break;
136-
case ImageFormat.Rgba32:
137-
alphaFormat = AlphaFormat.Premul;
138-
break;
139-
default:
140-
return new ImageSource(null, 0);
141-
}
142-
143-
var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(data, 0);
144-
var pixelSize = new PixelSize(pfiImage.Width, pfiImage.Height);
145-
var dpi = new Vector(96, 96);
146-
var bitmap = new Bitmap(pixelFormat, alphaFormat, ptr, pixelSize, dpi, stride);
147-
return new ImageSource(bitmap, size);
111+
var pixelFormat = PixelFormats.Bgra8888;
112+
var alphaFormat = AlphaFormat.Opaque;
113+
switch (pfiImage.Format)
114+
{
115+
case ImageFormat.Rgb8:
116+
pixelFormat = PixelFormats.Gray8;
117+
break;
118+
case ImageFormat.R5g5b5:
119+
case ImageFormat.R5g5b5a1:
120+
pixelFormat = PixelFormats.Bgr555;
121+
break;
122+
case ImageFormat.R5g6b5:
123+
pixelFormat = PixelFormats.Bgr565;
124+
break;
125+
case ImageFormat.Rgb24:
126+
pixelFormat = PixelFormats.Bgr24;
127+
break;
128+
case ImageFormat.Rgba16:
129+
var pixels2 = pfiImage.DataLen / 2;
130+
data = new byte[pixels2 * 4];
131+
stride = pfiImage.Width * 4;
132+
for (var i = 0; i < pixels2; i++)
133+
{
134+
var src = BitConverter.ToUInt16(pfiImage.Data, i * 2);
135+
data[i * 4 + 0] = (byte)Math.Round((src & 0x0F) / 15F * 255); // B
136+
data[i * 4 + 1] = (byte)Math.Round(((src >> 4) & 0x0F) / 15F * 255); // G
137+
data[i * 4 + 2] = (byte)Math.Round(((src >> 8) & 0x0F) / 15F * 255); // R
138+
data[i * 4 + 3] = (byte)Math.Round(((src >> 12) & 0x0F) / 15F * 255); // A
139+
}
140+
141+
alphaFormat = AlphaFormat.Premul;
142+
break;
143+
case ImageFormat.Rgba32:
144+
alphaFormat = AlphaFormat.Premul;
145+
break;
146+
default:
147+
return new ImageSource(null, 0);
148148
}
149+
150+
var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(data, 0);
151+
var pixelSize = new PixelSize(pfiImage.Width, pfiImage.Height);
152+
var dpi = new Vector(96, 96);
153+
var bitmap = new Bitmap(pixelFormat, alphaFormat, ptr, pixelSize, dpi, stride);
154+
return new ImageSource(bitmap, size);
149155
}
150-
catch
156+
}
157+
158+
private static ImageSource DecodeWithTiff(Stream stream, long size)
159+
{
160+
using (var tiff = Tiff.ClientOpen($"{Guid.NewGuid()}.tif", "r", stream, new TiffStream()))
151161
{
152-
return new ImageSource(null, 0);
162+
if (tiff == null)
163+
return new ImageSource(null, 0);
164+
165+
var width = tiff.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
166+
var height = tiff.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
167+
var pixels = new int[width * height];
168+
169+
// Currently only supports image when its `BITSPERSAMPLE` is one in [1,2,4,8,16]
170+
tiff.ReadRGBAImageOriented(width, height, pixels, Orientation.TOPLEFT);
171+
172+
var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(pixels, 0);
173+
var pixelSize = new PixelSize(width, height);
174+
var dpi = new Vector(96, 96);
175+
var bitmap = new Bitmap(PixelFormats.Rgba8888, AlphaFormat.Premul, ptr, pixelSize, dpi, width * 4);
176+
return new ImageSource(bitmap, size);
153177
}
154178
}
155179
}

0 commit comments

Comments
 (0)