From 6730dbd20801ca8b9a18ed6501a7bbc84c745bfa Mon Sep 17 00:00:00 2001 From: Michael Cortez Date: Tue, 31 Oct 2006 20:08:02 +0000 Subject: [PATCH] + Added Async download support to AssetSystem/ImageManager.cs + Started converting IA_ImageTool/ImageTool.cs to use the JasperLibrary, but was getting errors TODO: Add an Async download option to the ImageTool download example application git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@448 52acb1d6-8a22-11de-b505-999d5b087335 --- libsecondlife-cs/AssetSystem/ImageManager.cs | 207 ++++++++++++++++-- .../examples/IA_ImageTool/IA_ImageTool.csproj | 4 + .../examples/IA_ImageTool/ImageTool.cs | 22 +- 3 files changed, 216 insertions(+), 17 deletions(-) diff --git a/libsecondlife-cs/AssetSystem/ImageManager.cs b/libsecondlife-cs/AssetSystem/ImageManager.cs index c63c90be..6da665ae 100644 --- a/libsecondlife-cs/AssetSystem/ImageManager.cs +++ b/libsecondlife-cs/AssetSystem/ImageManager.cs @@ -26,6 +26,7 @@ using System; using System.Collections.Generic; +using System.IO; using libsecondlife; using libsecondlife.Packets; @@ -34,17 +35,26 @@ using libsecondlife.InventorySystem; namespace libsecondlife.AssetSystem { - /// + public delegate void ImageRetrievedCallback(LLUUID id, byte[] data); //this delegate is called when an image completed. + + /// /// Manages the uploading and downloading of Images from SecondLife /// public class ImageManager { private SecondLife slClient; + public enum CacheTypes {None, Memory, Disk}; + private CacheTypes CacheType; + private string CacheDirectory = "ImageCache"; + private Dictionary CacheTable = new Dictionary(); + private ImagePacketHelpers ImagePacketHelper; private Dictionary htDownloadRequests = new Dictionary(); + public ImageRetrievedCallback OnImageRetrieved; + private class TransferRequest { public bool Completed; @@ -75,18 +85,122 @@ namespace libsecondlife.AssetSystem /// public ImageManager(SecondLife client) { - slClient = client; + Init(client, CacheTypes.None, null); + } + /// + /// + /// + /// The type of Cache system to use for images. + public ImageManager(SecondLife client, CacheTypes ctype) + { + Init(client, ctype, null); + } + + /// + /// + /// + /// The type of Cache system to use for images. + /// The directory to use for disk based caching. + public ImageManager(SecondLife client, CacheTypes ctype, String directory) + { + Init(client, ctype, directory); + } + + /// + /// + /// + /// The type of Cache system to use for images. + /// The directory to use for disk based caching. + private void Init(SecondLife client, CacheTypes ctype, string directory) + { + slClient = client; + + // Setup Image Caching + CacheType = ctype; + if (ctype == CacheTypes.Disk) + { + if (directory != null) + { + CacheDirectory = directory; + } + + try + { + if (!Directory.Exists(CacheDirectory)) + { + Directory.CreateDirectory(CacheDirectory); + } + } + catch (Exception e) + { + Console.WriteLine("ERROR: Disk Cache directory could not be established, defaulting to Memory Cache."); + Console.WriteLine(e.Message); + + CacheType = CacheTypes.Memory; + } + + } + + // Image Packet Helpers ImagePacketHelper = new ImagePacketHelpers(client.Network.AgentID, client.Network.SessionID); - PacketCallback ImageDataCallback = new PacketCallback(ImageDataCallbackHandler); - slClient.Network.RegisterCallback(PacketType.ImageData, ImageDataCallback); + // Image Callbacks + slClient.Network.RegisterCallback(PacketType.ImageData, new PacketCallback(ImageDataCallbackHandler)); + slClient.Network.RegisterCallback(PacketType.ImagePacket, new PacketCallback(ImagePacketCallbackHandler)); + slClient.Network.RegisterCallback(PacketType.ImageNotInDatabase, new PacketCallback(ImageNotInDatabaseCallbackHandler)); + } - PacketCallback ImagePacketCallback = new PacketCallback(ImagePacketCallbackHandler); - slClient.Network.RegisterCallback(PacketType.ImagePacket, ImagePacketCallback); + private void CacheImage(LLUUID ImageID, byte[] ImageData) + { + switch (CacheType) + { + case CacheTypes.Memory: + CacheTable[ImageID] = ImageData; + break; + case CacheTypes.Disk: + String filepath = Path.Combine(CacheDirectory, ImageID.ToStringHyphenated()); + File.WriteAllBytes(filepath, ImageData); + break; + default: + break; + } + } - PacketCallback ImageNotInDatabaseCallback = new PacketCallback(ImageNotInDatabaseCallbackHandler); - slClient.Network.RegisterCallback(PacketType.ImageNotInDatabase, ImageNotInDatabaseCallback); + private byte[] CachedImage(LLUUID ImageID) + { + switch (CacheType) + { + case CacheTypes.Memory: + if (CacheTable.ContainsKey(ImageID)) + { + return CacheTable[ImageID]; + } + else + { + return null; + } + + + case CacheTypes.Disk: + String filepath = Path.Combine(CacheDirectory, ImageID.ToStringHyphenated()); + if (File.Exists(filepath)) + { + return File.ReadAllBytes(filepath); + } + else + { + return null; + } + + default: + return null; + } + } + + public bool isDownloadingImages() + { + return htDownloadRequests.Count > 0; } /// @@ -95,6 +209,12 @@ namespace libsecondlife.AssetSystem /// The Image's AssetID public byte[] RequestImage(LLUUID ImageID) { + byte[] imgData = CachedImage(ImageID); + if (imgData != null) + { + return imgData; + } + TransferRequest tr = new TransferRequest(); tr.Completed = false; tr.Size = int.MaxValue; // Number of bytes expected @@ -113,15 +233,39 @@ namespace libsecondlife.AssetSystem if( tr.Status == true ) { + CacheImage(ImageID, tr.AssetData); return tr.AssetData; } else { throw new Exception( "RequestImage: " + tr.StatusMsg ); } - } + /// + /// Requests an image from SecondLife. + /// + /// The Image's AssetID + public void RequestImageAsync(LLUUID ImageID) + { + byte[] imgData = CachedImage(ImageID); + if (imgData != null) + { + FireImageRetrieved(ImageID, 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 + + htDownloadRequests[ImageID] = tr; + + Packet packet = ImagePacketHelper.RequestImage(ImageID); + slClient.Network.SendPacket(packet); + } + /// /// Handles the Image Data packet, which includes the ID, and Size of the image, @@ -140,7 +284,7 @@ namespace libsecondlife.AssetSystem byte[] Data = reply.ImageData.Data; // Lookup the request that this packet is for - TransferRequest tr = (TransferRequest)htDownloadRequests[ImageID]; + TransferRequest tr = htDownloadRequests[ImageID]; if( tr == null ) { // Received a packet for an image we didn't request... @@ -159,8 +303,11 @@ namespace libsecondlife.AssetSystem // If we've gotten all the data, mark it completed. if( tr.Received >= tr.Size ) { - tr.Completed = true; tr.Status = true; + tr.Completed = true; + + // Fire off image downloaded event + FireImageRetrieved(ImageID, tr.AssetData); } } @@ -173,9 +320,10 @@ namespace libsecondlife.AssetSystem { ImagePacketPacket reply = (ImagePacketPacket)packet; + LLUUID ImageID = reply.ImageID.ID; // Lookup the request for this packet - TransferRequest tr = (TransferRequest)htDownloadRequests[reply.ImageID.ID]; + TransferRequest tr = (TransferRequest)htDownloadRequests[ImageID]; if( tr == null ) { // Received a packet that doesn't belong to any requests in our queue, strange... @@ -195,8 +343,11 @@ namespace libsecondlife.AssetSystem // If we've gotten all the data, mark it completed. if( tr.Received >= tr.Size ) { - tr.Completed = true; - tr.Status = true; + tr.Status = true; + tr.Completed = true; + + // Fire off image downloaded event + FireImageRetrieved(ImageID, tr.AssetData); } } @@ -205,7 +356,33 @@ namespace libsecondlife.AssetSystem /// public void ImageNotInDatabaseCallbackHandler(Packet packet, Simulator simulator) { - Console.WriteLine(packet); + ImageNotInDatabasePacket reply = (ImageNotInDatabasePacket)packet; + + LLUUID ImageID = reply.ImageID.ID; + + // Lookup the request for this packet + TransferRequest tr = (TransferRequest)htDownloadRequests[ImageID]; + if( tr == null ) + { + // Received a packet that doesn't belong to any requests in our queue, strange... + return; + } + + tr.Status = false; + tr.StatusMsg = "Image not in database"; + tr.Completed = true; + + // Fire off image downloaded event + FireImageRetrieved(ImageID, null); } + + private void FireImageRetrieved(LLUUID ImageID, byte[] ImageData) + { + if (OnImageRetrieved != null) + { + OnImageRetrieved(ImageID, ImageData); + } + } + } } \ No newline at end of file diff --git a/libsecondlife-cs/examples/IA_ImageTool/IA_ImageTool.csproj b/libsecondlife-cs/examples/IA_ImageTool/IA_ImageTool.csproj index 7d18e0b1..7e8fb464 100644 --- a/libsecondlife-cs/examples/IA_ImageTool/IA_ImageTool.csproj +++ b/libsecondlife-cs/examples/IA_ImageTool/IA_ImageTool.csproj @@ -39,6 +39,10 @@ + + {7D4C4807-7705-48A7-9D82-F6689FBBCC8B} + libjaspernet + {E464B963-46E3-4E1A-A36F-9C640C880E68} IA_SimpleInventory diff --git a/libsecondlife-cs/examples/IA_ImageTool/ImageTool.cs b/libsecondlife-cs/examples/IA_ImageTool/ImageTool.cs index 6da6ea4d..56e987f2 100644 --- a/libsecondlife-cs/examples/IA_ImageTool/ImageTool.cs +++ b/libsecondlife-cs/examples/IA_ImageTool/ImageTool.cs @@ -8,6 +8,8 @@ using libsecondlife; using libsecondlife.InventorySystem; using libsecondlife.AssetSystem; + + namespace IA_ImageTool { /// @@ -25,8 +27,12 @@ namespace IA_ImageTool [STAThread] static new void Main(string[] args) { - if (KakaduWrap.Check4Tools() == false) + if ( +// (File.Exists("libjasper.dll") == false) && + (KakaduWrap.Check4Tools() == false) + ) { +// Console.WriteLine("or you need a copy of libjasper.dll, it can be found in SVN in the main trunk inside libjaspernet"); return; } @@ -65,6 +71,7 @@ namespace IA_ImageTool } ImageTool it = new ImageTool(id, filename, put); + it.DownloadInventoryOnConnect = false; it.Connect(args[0], args[1], args[2]); it.doStuff(); it.Disconnect(); @@ -101,16 +108,27 @@ namespace IA_ImageTool int start = Environment.TickCount; - ImageManager im = new ImageManager(base.client); + ImageManager im = new ImageManager(base.client, ImageManager.CacheTypes.Disk); byte[] j2cdata = im.RequestImage(_ImageID); int end = Environment.TickCount; Console.WriteLine("Elapsed download time, in TickCounts: " + (end - start)); + Console.WriteLine("Image Data Length :" + j2cdata.Length); + Console.WriteLine("Writing to: " + _FileName + ".tif"); KakaduWrap.WriteJ2CAsTiff(_FileName + ".tif", j2cdata); Console.WriteLine("Writing to: " + _FileName + ".bmp"); KakaduWrap.WriteJ2CAsBmp(_FileName + ".bmp", j2cdata); + + /* + Console.WriteLine("Writing to: " + _FileName + ".tif"); + File.WriteAllBytes(_FileName + ".tif", JasperWrapper.jasper_decode_j2c_to_tiff(j2cdata)); + + Console.WriteLine("Writing to: " + _FileName + ".tga"); + File.WriteAllBytes(_FileName + ".tga", JasperWrapper.jasper_decode_j2c_to_tga(j2cdata)); + */ + } Console.WriteLine("Done...");