From 839c7cfd7d7ac8335db1230f77cd1d9c7dae67bb Mon Sep 17 00:00:00 2001 From: Michael Cortez Date: Wed, 1 Nov 2006 00:14:43 +0000 Subject: [PATCH] + IA_TestAsyncImage tests the async download code of ImageManager, by downloading all textures encountered when listening to OnNewPrim + Removed KakaduWrap, no longer needed now that ImageTool uses Jasper + Exposed an accessor for Textures in TextureEntry git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@454 52acb1d6-8a22-11de-b505-999d5b087335 --- libsecondlife-cs/AssetSystem/ImageManager.cs | 114 +++++++++---- .../AssetSystem/ImagePacketHelpers.cs | 1 - libsecondlife-cs/Textures.cs | 4 + .../examples/IA_ImageTool/IA_ImageTool.csproj | 1 + .../examples/IA_ImageTool/KakaduWrap.cs | 150 ------------------ .../IA_TestAsyncImage/IA_TestAsyncImage.cs | 141 ++++++++++++++++ .../IA_TestAsyncImage.csproj | 62 ++++++++ .../Properties/AssemblyInfo.cs | 33 ++++ libsecondlife-cs/libsecondlife.sln | 6 + 9 files changed, 331 insertions(+), 181 deletions(-) delete mode 100644 libsecondlife-cs/examples/IA_ImageTool/KakaduWrap.cs create mode 100644 libsecondlife-cs/examples/IA_TestAsyncImage/IA_TestAsyncImage.cs create mode 100644 libsecondlife-cs/examples/IA_TestAsyncImage/IA_TestAsyncImage.csproj create mode 100644 libsecondlife-cs/examples/IA_TestAsyncImage/Properties/AssemblyInfo.cs diff --git a/libsecondlife-cs/AssetSystem/ImageManager.cs b/libsecondlife-cs/AssetSystem/ImageManager.cs index 6da665ae..6811dd00 100644 --- a/libsecondlife-cs/AssetSystem/ImageManager.cs +++ b/libsecondlife-cs/AssetSystem/ImageManager.cs @@ -35,7 +35,7 @@ using libsecondlife.InventorySystem; namespace libsecondlife.AssetSystem { - public delegate void ImageRetrievedCallback(LLUUID id, byte[] data); //this delegate is called when an image completed. + public delegate void ImageRetrievedCallback(LLUUID id, byte[] data, bool cached); //this delegate is called when an image completed. /// /// Manages the uploading and downloading of Images from SecondLife @@ -180,8 +180,6 @@ namespace libsecondlife.AssetSystem { return null; } - - case CacheTypes.Disk: String filepath = Path.Combine(CacheDirectory, ImageID.ToStringHyphenated()); if (File.Exists(filepath)) @@ -198,9 +196,28 @@ namespace libsecondlife.AssetSystem } } + public bool isCachedImage(LLUUID ImageID) + { + switch (CacheType) + { + case CacheTypes.Memory: + return CacheTable.ContainsKey(ImageID); + case CacheTypes.Disk: + String filepath = Path.Combine(CacheDirectory, ImageID.ToStringHyphenated()); + return File.Exists(filepath); + default: + return false; + } + } + public bool isDownloadingImages() { - return htDownloadRequests.Count > 0; + bool isDownloading = false; + lock (htDownloadRequests) + { + isDownloading = htDownloadRequests.Count > 0 ? true : false; + } + return isDownloading; } /// @@ -215,16 +232,27 @@ namespace libsecondlife.AssetSystem return imgData; } - TransferRequest tr = new TransferRequest(); - tr.Completed = false; - tr.Size = int.MaxValue; // Number of bytes expected - tr.Received = 0; // Number of bytes received - tr.LastPacket = Helpers.GetUnixTime(); // last time we recevied a packet for this request + TransferRequest tr; + lock (htDownloadRequests) + { + if (htDownloadRequests.ContainsKey(ImageID) == false) + { + tr = new TransferRequest(); + tr.Completed = false; + tr.Size = int.MaxValue; // Number of bytes expected + tr.Received = 0; // Number of bytes received + tr.LastPacket = Helpers.GetUnixTime(); // last time we recevied a packet for this request - htDownloadRequests[ImageID] = tr; + htDownloadRequests[ImageID] = tr; - Packet packet = ImagePacketHelper.RequestImage(ImageID); - slClient.Network.SendPacket(packet); + Packet packet = ImagePacketHelper.RequestImage(ImageID); + slClient.Network.SendPacket(packet); + } + else + { + tr = htDownloadRequests[ImageID]; + } + } while( tr.Completed == false ) { @@ -233,7 +261,6 @@ namespace libsecondlife.AssetSystem if( tr.Status == true ) { - CacheImage(ImageID, tr.AssetData); return tr.AssetData; } else @@ -248,22 +275,33 @@ namespace libsecondlife.AssetSystem /// The Image's AssetID public void RequestImageAsync(LLUUID ImageID) { + if (ImageID == null) + { + throw new Exception("WTF!!! Don't request Image Assets by passing in an ImageID of null"); + } + byte[] imgData = CachedImage(ImageID); if (imgData != null) { - FireImageRetrieved(ImageID, imgData); + FireImageRetrieved(ImageID, imgData, true); } - TransferRequest tr = new TransferRequest(); - tr.Completed = false; - tr.Size = int.MaxValue; // Number of bytes expected - tr.Received = 0; // Number of bytes received - tr.LastPacket = Helpers.GetUnixTime(); // last time we recevied a packet for this request + lock (htDownloadRequests) + { + if (htDownloadRequests.ContainsKey(ImageID) == false) + { + TransferRequest tr = new TransferRequest(); + tr.Completed = false; + tr.Size = int.MaxValue; // Number of bytes expected + tr.Received = 0; // Number of bytes received + tr.LastPacket = Helpers.GetUnixTime(); // last time we recevied a packet for this request - htDownloadRequests[ImageID] = tr; + htDownloadRequests[ImageID] = tr; - Packet packet = ImagePacketHelper.RequestImage(ImageID); - slClient.Network.SendPacket(packet); + Packet packet = ImagePacketHelper.RequestImage(ImageID); + slClient.Network.SendPacket(packet); + } + } } @@ -284,7 +322,11 @@ namespace libsecondlife.AssetSystem byte[] Data = reply.ImageData.Data; // Lookup the request that this packet is for - TransferRequest tr = htDownloadRequests[ImageID]; + TransferRequest tr; + lock (htDownloadRequests) + { + tr = htDownloadRequests[ImageID]; + } if( tr == null ) { // Received a packet for an image we didn't request... @@ -307,7 +349,8 @@ namespace libsecondlife.AssetSystem tr.Completed = true; // Fire off image downloaded event - FireImageRetrieved(ImageID, tr.AssetData); + CacheImage(ImageID, tr.AssetData); + FireImageRetrieved(ImageID, tr.AssetData, false); } } @@ -323,7 +366,11 @@ namespace libsecondlife.AssetSystem LLUUID ImageID = reply.ImageID.ID; // Lookup the request for this packet - TransferRequest tr = (TransferRequest)htDownloadRequests[ImageID]; + TransferRequest tr; + lock (htDownloadRequests) + { + tr = (TransferRequest)htDownloadRequests[ImageID]; + } if( tr == null ) { // Received a packet that doesn't belong to any requests in our queue, strange... @@ -335,6 +382,8 @@ namespace libsecondlife.AssetSystem // then once we've received all data packets, it should be re-assembled into a complete array and marked // completed. + // FIXME: Sometimes this gets called before ImageDataCallbackHandler, when that + // happens tr.AssetData will be null. Implimenting the above TODO should fix this. // Add this packet's data to the request. Array.Copy(reply.ImageData.Data, 0, tr.AssetData, tr.BaseDataReceived + (1000 * (reply.ImageID.Packet - 1)), reply.ImageData.Data.Length); @@ -347,7 +396,8 @@ namespace libsecondlife.AssetSystem tr.Completed = true; // Fire off image downloaded event - FireImageRetrieved(ImageID, tr.AssetData); + CacheImage(ImageID, tr.AssetData); + FireImageRetrieved(ImageID, tr.AssetData, false); } } @@ -361,7 +411,11 @@ namespace libsecondlife.AssetSystem LLUUID ImageID = reply.ImageID.ID; // Lookup the request for this packet - TransferRequest tr = (TransferRequest)htDownloadRequests[ImageID]; + TransferRequest tr; + lock (htDownloadRequests) + { + tr = (TransferRequest)htDownloadRequests[ImageID]; + } if( tr == null ) { // Received a packet that doesn't belong to any requests in our queue, strange... @@ -373,14 +427,14 @@ namespace libsecondlife.AssetSystem tr.Completed = true; // Fire off image downloaded event - FireImageRetrieved(ImageID, null); + FireImageRetrieved(ImageID, null, false); } - private void FireImageRetrieved(LLUUID ImageID, byte[] ImageData) + private void FireImageRetrieved(LLUUID ImageID, byte[] ImageData, bool cached) { if (OnImageRetrieved != null) { - OnImageRetrieved(ImageID, ImageData); + OnImageRetrieved(ImageID, ImageData, cached); } } diff --git a/libsecondlife-cs/AssetSystem/ImagePacketHelpers.cs b/libsecondlife-cs/AssetSystem/ImagePacketHelpers.cs index 89b8866d..fcb1e798 100644 --- a/libsecondlife-cs/AssetSystem/ImagePacketHelpers.cs +++ b/libsecondlife-cs/AssetSystem/ImagePacketHelpers.cs @@ -63,7 +63,6 @@ namespace libsecondlife.AssetSystem p.RequestImage[0].Image = imageID; //FIXME: Find out what the appropriate values are here. - Console.WriteLine("Don't know what the valid value should be for Type in RequestImage"); p.RequestImage[0].Type = 0; p.AgentData.AgentID = AgentID; diff --git a/libsecondlife-cs/Textures.cs b/libsecondlife-cs/Textures.cs index 2dd9b8e9..d95f0157 100644 --- a/libsecondlife-cs/Textures.cs +++ b/libsecondlife-cs/Textures.cs @@ -42,6 +42,10 @@ namespace libsecondlife private SecondLife Client; private Dictionary Textures; + public Dictionary FaceTextures + { + get { return Textures; } + } /// /// diff --git a/libsecondlife-cs/examples/IA_ImageTool/IA_ImageTool.csproj b/libsecondlife-cs/examples/IA_ImageTool/IA_ImageTool.csproj index 7e8fb464..9abad996 100644 --- a/libsecondlife-cs/examples/IA_ImageTool/IA_ImageTool.csproj +++ b/libsecondlife-cs/examples/IA_ImageTool/IA_ImageTool.csproj @@ -37,6 +37,7 @@ + diff --git a/libsecondlife-cs/examples/IA_ImageTool/KakaduWrap.cs b/libsecondlife-cs/examples/IA_ImageTool/KakaduWrap.cs deleted file mode 100644 index 2015daf5..00000000 --- a/libsecondlife-cs/examples/IA_ImageTool/KakaduWrap.cs +++ /dev/null @@ -1,150 +0,0 @@ -using System; -using System.IO; -using System.Diagnostics; - -namespace IA_ImageTool -{ - /// - /// Summary description for ImageTools. - /// - public class KakaduWrap - { - private KakaduWrap() - { - } - - public static bool Check4Tools() - { - bool status = true; - if( File.Exists("kdu_expand.exe") == false ) - { - status = false; - Console.WriteLine("You need kdu_expand.exe to save SL images."); - } - - if( File.Exists("kdu_compress.exe") == false ) - { - status = false; - Console.WriteLine("You need kdu_compress.exe to load images into SL."); - } - - return status; - } - - public static void WriteJ2CToFile( string j2c_filename, byte[] J2CData ) - { - FileStream fs = System.IO.File.OpenWrite( j2c_filename ); - fs.Write(J2CData, 0, J2CData.Length); - fs.Close(); - } - - - public static void Convert2Tiff(string j2c_filename, string tif_filename) - { - if (File.Exists("kdu_expand.exe") == false) - { - throw new Exception("You must have kdu_expand.exe"); - } - - if (tif_filename.ToLower().EndsWith(".tif") == false) - { - tif_filename += ".tif"; - } - - string args = "-i " + j2c_filename + " -o " + tif_filename; - Console.WriteLine(args); - - Process p = new Process(); - p.StartInfo.UseShellExecute = false; - p.StartInfo.FileName = "kdu_expand.exe"; - p.StartInfo.Arguments = args; - p.Start(); - p.WaitForExit(); - } - - public static void Convert2Bmp(string j2c_filename, string bmp_filename) - { - if (File.Exists("kdu_expand.exe") == false) - { - throw new Exception("You must have kdu_expand.exe"); - } - - if (bmp_filename.ToLower().EndsWith(".bmp") == false) - { - bmp_filename += ".bmp"; - } - - string args = "-i " + j2c_filename + " -o " + bmp_filename; - Console.WriteLine(args); - - Process p = new Process(); - p.StartInfo.UseShellExecute = false; - p.StartInfo.FileName = "kdu_expand.exe"; - p.StartInfo.Arguments = args; - p.Start(); - p.WaitForExit(); - } - - public static void WriteJ2CAsTiff(string tif_filename, byte[] J2CData) - { - String tempname = tif_filename + ".j2c"; - WriteJ2CToFile( tempname, J2CData ); - Convert2Tiff( tempname, tif_filename ); - File.Delete( tempname ); - } - - public static void WriteJ2CAsBmp(string bmp_filename, byte[] J2CData) - { - String tempname = bmp_filename + ".j2c"; - WriteJ2CToFile(tempname, J2CData); - Convert2Bmp(tempname, bmp_filename); - File.Delete(tempname); - } - - /* - * kdu_compress -no_info -no_weights -no_palette -i TestTexture.tif -o TestTexture.J2C - */ - public static void Convert2J2C( string tif_filename, string j2c_filename ) - { - if( File.Exists("kdu_compress.exe") == false ) - { - throw new Exception("You must have kdu_compress.exe"); - } - - if( j2c_filename.ToLower().EndsWith(".j2c") == false ) - { - j2c_filename += ".j2c"; - } - - Process p = new Process(); - p.StartInfo.UseShellExecute = false; - p.StartInfo.FileName = "kdu_compress.exe"; - p.StartInfo.Arguments = "-no_info -no_weights -no_palette -i " + tif_filename + " -o " + j2c_filename; - p.Start(); - p.WaitForExit(); - } - - public static byte[] ReadJ2CData( string filename ) - { - if( (filename.ToLower().EndsWith(".j2c") == false) && (filename.ToLower().EndsWith(".tif") == true) ) - { - string tempname = filename + ".j2c"; - Convert2J2C( filename, tempname ); - filename = tempname; - } - - FileStream fs = File.OpenRead( filename ); - - byte[] data = new byte[fs.Length]; - - if( fs.Length > int.MaxValue ) - { - throw new Exception("AssetImage.cs: Bad stuff going to happen because length bigger then Max Integer"); - } - - fs.Read(data, 0, (int)fs.Length); - - return data; - } - } -} diff --git a/libsecondlife-cs/examples/IA_TestAsyncImage/IA_TestAsyncImage.cs b/libsecondlife-cs/examples/IA_TestAsyncImage/IA_TestAsyncImage.cs new file mode 100644 index 00000000..dc8328b6 --- /dev/null +++ b/libsecondlife-cs/examples/IA_TestAsyncImage/IA_TestAsyncImage.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using IA_SimpleInventory; + +using libsecondlife; +using libsecondlife.AssetSystem; + +namespace IA_TestAsyncImage +{ + class TestAsync : SimpleInventory + { + ImageManager imgManager; + + Queue TextureQueue = new Queue(); + + string OutputDirectory = "IA_TestAsyncImages"; + + [STAThread] + static new void Main(string[] args) + { + TestAsync app = new TestAsync(); + app.DownloadInventoryOnConnect = false; + + app.client.Objects.OnNewPrim += new NewPrimCallback(app.Objects_OnNewPrim); + app.client.Objects.OnNewAvatar += new NewAvatarCallback(app.Objects_OnNewAvatar); + + + app.Connect(args[0], args[1], args[2]); + app.doStuff(); + app.Disconnect(); + + System.Threading.Thread.Sleep(500); + + Console.WriteLine("Done..."); + } + + private void Objects_OnNewAvatar(Simulator simulator, Avatar avatar, ulong regionHandle, ushort timeDilation) + { + if (imgManager == null) + { + Console.WriteLine("ImageManager not ready yet, queueing Avatar textures."); + TextureQueue.Enqueue(avatar.FirstLifeImage); + TextureQueue.Enqueue(avatar.ProfileImage); + } + else + { + if (avatar.FirstLifeImage != null) + { + imgManager.RequestImageAsync(avatar.FirstLifeImage); + } + + if (avatar.ProfileImage != null) + { + imgManager.RequestImageAsync(avatar.ProfileImage); + } + } + } + + private void Objects_OnNewPrim(Simulator simulator, PrimObject prim, ulong regionHandle, ushort timeDilation) + { + if (imgManager == null) + { + Console.WriteLine("ImageManager not ready yet, queueing Prim textures."); + TextureQueue.Enqueue(prim.Textures.DefaultTexture.TextureID); + + foreach (TextureEntryFace tef in prim.Textures.FaceTextures.Values) + { + TextureQueue.Enqueue(tef.TextureID); + } + } + else + { + if ((prim.Textures.DefaultTexture != null) && (prim.Textures.DefaultTexture.TextureID != null)) + { + imgManager.RequestImageAsync(prim.Textures.DefaultTexture.TextureID); + } + + if (prim.Textures.FaceTextures != null) + { + foreach (TextureEntryFace tef in prim.Textures.FaceTextures.Values) + { + imgManager.RequestImageAsync(tef.TextureID); + } + } + } + } + + private void NewImageRetrievedCallBack( LLUUID ImageID, byte[] data, bool wasCached ) + { + if (wasCached) + { + Console.WriteLine("Cache ( " + data.Length + "): " + ImageID); + } + else + { + Console.WriteLine("Finished ( " + data.Length + "): " + ImageID); + + String filename = Path.Combine(OutputDirectory, ImageID.ToStringHyphenated()) + ".tif"; + + TiffJob tj = new TiffJob( filename, data ); + Thread t = new Thread(tj.RunMe); + t.Start(); + + } + } + + protected new void doStuff() + { + imgManager = new ImageManager(client, ImageManager.CacheTypes.Disk, OutputDirectory); + imgManager.OnImageRetrieved += new ImageRetrievedCallback(NewImageRetrievedCallBack); + + while (TextureQueue.Count > 0) + { + imgManager.RequestImageAsync(TextureQueue.Dequeue()); + } + + Console.WriteLine("Press any key to stop."); + Console.Read(); + } + + protected class TiffJob + { + string filename; + byte[] j2cdata; + + public TiffJob(string path, byte[] data) + { + filename = path; + j2cdata = data; + } + + public void RunMe() + { + File.WriteAllBytes(filename, JasperWrapper.jasper_decode_j2c_to_tiff(j2cdata)); + } + } + } +} diff --git a/libsecondlife-cs/examples/IA_TestAsyncImage/IA_TestAsyncImage.csproj b/libsecondlife-cs/examples/IA_TestAsyncImage/IA_TestAsyncImage.csproj new file mode 100644 index 00000000..346aa7c5 --- /dev/null +++ b/libsecondlife-cs/examples/IA_TestAsyncImage/IA_TestAsyncImage.csproj @@ -0,0 +1,62 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {A4F59DE9-E382-401D-AA8D-4557779D764E} + Exe + Properties + IA_TestAsyncImage + IA_TestAsyncImage + IA_TestAsyncImage.TestAsync + + + true + full + false + ..\..\..\bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + {7D4C4807-7705-48A7-9D82-F6689FBBCC8B} + libjaspernet + + + {D9CDEDFB-8169-4B03-B57F-0DF638F044EC} + libsecondlife + + + {E464B963-46E3-4E1A-A36F-9C640C880E68} + IA_SimpleInventory + + + + + \ No newline at end of file diff --git a/libsecondlife-cs/examples/IA_TestAsyncImage/Properties/AssemblyInfo.cs b/libsecondlife-cs/examples/IA_TestAsyncImage/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..a540f81e --- /dev/null +++ b/libsecondlife-cs/examples/IA_TestAsyncImage/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("IA_TestAsyncImage")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("IA_TestAsyncImage")] +[assembly: AssemblyCopyright("Copyright © 2006")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("224dd7e4-244c-4f89-8e5a-bc84a636a7b9")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/libsecondlife-cs/libsecondlife.sln b/libsecondlife-cs/libsecondlife.sln index f15f5d91..4b9e883c 100644 --- a/libsecondlife-cs/libsecondlife.sln +++ b/libsecondlife-cs/libsecondlife.sln @@ -38,6 +38,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "libjaspernet", "..\libjaspe EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastImageApp", "examples\FastImageApp\FastImageApp.csproj", "{74E4D0EA-93C2-40BC-A075-9EAD3A8FFCF1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IA_TestAsyncImage", "examples\IA_TestAsyncImage\IA_TestAsyncImage.csproj", "{A4F59DE9-E382-401D-AA8D-4557779D764E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -120,6 +122,10 @@ Global {74E4D0EA-93C2-40BC-A075-9EAD3A8FFCF1}.Debug|Any CPU.Build.0 = Debug|Any CPU {74E4D0EA-93C2-40BC-A075-9EAD3A8FFCF1}.Release|Any CPU.ActiveCfg = Release|Any CPU {74E4D0EA-93C2-40BC-A075-9EAD3A8FFCF1}.Release|Any CPU.Build.0 = Release|Any CPU + {A4F59DE9-E382-401D-AA8D-4557779D764E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A4F59DE9-E382-401D-AA8D-4557779D764E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4F59DE9-E382-401D-AA8D-4557779D764E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4F59DE9-E382-401D-AA8D-4557779D764E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE