From accae0b85413ef45fa76a593ad75e3aa005d1ea3 Mon Sep 17 00:00:00 2001 From: Cinder Roxley Date: Sat, 3 Aug 2024 10:04:31 -0500 Subject: [PATCH] Restore TGA export support --- LibreMetaverse/Imaging/ManagedImage.cs | 3 +- LibreMetaverse/Imaging/Targa.cs | 69 ++++++++++++++++++- .../Commands/Inventory/DumpOutfitCommand.cs | 16 +++-- .../Commands/Prims/ExportCommand.cs | 2 +- 4 files changed, 80 insertions(+), 10 deletions(-) diff --git a/LibreMetaverse/Imaging/ManagedImage.cs b/LibreMetaverse/Imaging/ManagedImage.cs index 1361750f..39f3fc1e 100644 --- a/LibreMetaverse/Imaging/ManagedImage.cs +++ b/LibreMetaverse/Imaging/ManagedImage.cs @@ -118,7 +118,7 @@ namespace OpenMetaverse.Imaging /// Constructs ManagedImage class from /// Currently only supporting 8-bit channels; /// - /// Input + /// Input public ManagedImage(PortableImage image) { Width = image.Width; @@ -436,6 +436,7 @@ namespace OpenMetaverse.Imaging return SKBitmap.FromImage(img); } + [Obsolete("ExportTGA() is deprecated, please use Targa.Encode() instead.")] public byte[] ExportTGA() { byte[] tga = new byte[Width * Height * ((Channels & ImageChannels.Alpha) == 0 ? 3 : 4) + 32]; diff --git a/LibreMetaverse/Imaging/Targa.cs b/LibreMetaverse/Imaging/Targa.cs index 013ddda8..f91dbc95 100644 --- a/LibreMetaverse/Imaging/Targa.cs +++ b/LibreMetaverse/Imaging/Targa.cs @@ -32,8 +32,9 @@ using SkiaSharp; namespace OpenMetaverse.Imaging { - public class Targa + public static class Targa { + /// Decode Truvision TGA file to public static SKBitmap Decode(string fileName) { using (var image = Pfimage.FromFile(fileName)) @@ -42,6 +43,7 @@ namespace OpenMetaverse.Imaging } } + /// Decode Truvision TGA stream to public static SKBitmap Decode(Stream stream) { using (var image = Pfimage.FromStream(stream)) @@ -98,5 +100,70 @@ namespace OpenMetaverse.Imaging } } } + + /// Encode to Truvision TGA byte array + public static byte[] Encode(ManagedImage image) + { + var tga = new byte[image.Width * image.Height * ((image.Channels & ManagedImage.ImageChannels.Alpha) == 0 ? 3 : 4) + 32]; + var di = 0; + tga[di++] = 0; // idlength + tga[di++] = 0; // colormaptype = 0: no colormap + tga[di++] = 2; // image type = 2: uncompressed RGB + tga[di++] = 0; // color map spec is five zeroes for no color map + tga[di++] = 0; // color map spec is five zeroes for no color map + tga[di++] = 0; // color map spec is five zeroes for no color map + tga[di++] = 0; // color map spec is five zeroes for no color map + tga[di++] = 0; // color map spec is five zeroes for no color map + tga[di++] = 0; // x origin = two bytes + tga[di++] = 0; // x origin = two bytes + tga[di++] = 0; // y origin = two bytes + tga[di++] = 0; // y origin = two bytes + tga[di++] = (byte)(image.Width & 0xFF); // width - low byte + tga[di++] = (byte)(image.Width >> 8); // width - hi byte + tga[di++] = (byte)(image.Height & 0xFF); // height - low byte + tga[di++] = (byte)(image.Height >> 8); // height - hi byte + tga[di++] = (byte)((image.Channels & ManagedImage.ImageChannels.Alpha) == 0 ? 24 : 32); // 24/32 bits per pixel + tga[di++] = (byte)((image.Channels & ManagedImage.ImageChannels.Alpha) == 0 ? 32 : 40); // image descriptor byte + + int n = image.Width * image.Height; + + if ((image.Channels & ManagedImage.ImageChannels.Alpha) != 0) + { + if ((image.Channels & ManagedImage.ImageChannels.Color) != 0) + { + // RGBA + for (var i = 0; i < n; i++) + { + tga[di++] = image.Blue[i]; + tga[di++] = image.Green[i]; + tga[di++] = image.Red[i]; + tga[di++] = image.Alpha[i]; + } + } + else + { + // Alpha only + for (var i = 0; i < n; i++) + { + tga[di++] = image.Alpha[i]; + tga[di++] = image.Alpha[i]; + tga[di++] = image.Alpha[i]; + tga[di++] = byte.MaxValue; + } + } + } + else + { + // RGB + for (var i = 0; i < n; i++) + { + tga[di++] = image.Blue[i]; + tga[di++] = image.Green[i]; + tga[di++] = image.Red[i]; + } + } + + return tga; + } } } \ No newline at end of file diff --git a/Programs/examples/TestClient/Commands/Inventory/DumpOutfitCommand.cs b/Programs/examples/TestClient/Commands/Inventory/DumpOutfitCommand.cs index 95f38825..76c1b285 100644 --- a/Programs/examples/TestClient/Commands/Inventory/DumpOutfitCommand.cs +++ b/Programs/examples/TestClient/Commands/Inventory/DumpOutfitCommand.cs @@ -31,8 +31,10 @@ using System.IO; using System.Collections.Generic; using CSJ2K; using OpenMetaverse.Assets; +using OpenMetaverse.Imaging; using Pfim; using SkiaSharp; +using Targa = OpenMetaverse.Imaging.Targa; namespace OpenMetaverse.TestClient { @@ -119,13 +121,13 @@ namespace OpenMetaverse.TestClient File.WriteAllBytes(assetTexture.AssetID + ".jp2", assetTexture.AssetData); Console.WriteLine($"Wrote JPEG2000 image {assetTexture.AssetID}.jp2"); - // FIXME: Need to readd TARGA support! - //var bitmap = J2kImage.FromBytes(assetTexture.AssetData).As(); - //var image = SKImage.FromPixels(bitmap.PeekPixels()); - //var bytes = image.Encode(SKEncodedImageFormat.Tga, 100); - //File.WriteAllBytes(assetTexture.AssetID + ".tga", bytes.ToArray()); - // - //Console.WriteLine($"Wrote TGA image {assetTexture.AssetID}.tga"); + using (var bitmap = J2kImage.FromBytes(assetTexture.AssetData).As()) + { + var mi = new ManagedImage(bitmap); + var bytes = Targa.Encode(mi); + File.WriteAllBytes(assetTexture.AssetID + ".tga", bytes); + Console.WriteLine($"Wrote TGA image {assetTexture.AssetID}.tga"); + } } catch (Exception e) { diff --git a/Programs/examples/TestClient/Commands/Prims/ExportCommand.cs b/Programs/examples/TestClient/Commands/Prims/ExportCommand.cs index 7e2cc59e..fe720fc3 100644 --- a/Programs/examples/TestClient/Commands/Prims/ExportCommand.cs +++ b/Programs/examples/TestClient/Commands/Prims/ExportCommand.cs @@ -190,7 +190,7 @@ namespace OpenMetaverse.TestClient if (asset.Decode()) { - try { File.WriteAllBytes(asset.AssetID + ".tga", asset.Image.ExportTGA()); } + try { File.WriteAllBytes(asset.AssetID + ".tga", Imaging.Targa.Encode(asset.Image)); } catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client); } } else