using System; using System.Collections.Generic; using System.Linq; using System.Text; using Xenki.Framework; using OpenMetaverse; using System.Drawing; using System.IO; using System.Threading; using OpenMetaverse.Imaging; using System.Runtime.InteropServices; namespace Xenki.DefaultRenderer { public class DefaultTexture { private event TextureDownloadFinished onDownloadFinished; string cachefolderPath = System.Windows.Forms.Application.LocalUserAppDataPath + System.IO.Path.DirectorySeparatorChar + "cache"; // Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "textures"); string bmpFilePath = System.Windows.Forms.Application.LocalUserAppDataPath + System.IO.Path.DirectorySeparatorChar + "texture"; //Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "texture"); //the textures have load in memory. // private readonly Dictionary CurruntTextures = new Dictionary(); private Thread MainThread; // list of current requests in process private Dictionary CurrentRequests; private Queue RequestQueue; private static AutoResetEvent[] resetEvents; private static int[] threadpoolSlots; // maximum allowed concurrent requests at once const int MAX_TEXTURE_REQUESTS = 3; private GridClient gridClient; public GridClient GridClient { get { return gridClient; } set { gridClient = value; gridClient.Settings.USE_TEXTURE_CACHE = true; if (Directory.Exists(cachefolderPath) == false) Directory.CreateDirectory(cachefolderPath); if (Directory.Exists(bmpFilePath) == false) Directory.CreateDirectory(bmpFilePath); gridClient.Throttle.Texture = 440000.0f; gridClient.Settings.TEXTURE_CACHE_DIR = cachefolderPath; gridClient.Settings.TEXTURE_CACHE_MAX_SIZE = 5120;//M? or number? } } private static readonly DefaultTexture singleton = new DefaultTexture(); public static DefaultTexture SingleTextureManager() { return singleton; } private DefaultTexture() { RequestQueue = new Queue(); CurrentRequests = new Dictionary(MAX_TEXTURE_REQUESTS); resetEvents = new AutoResetEvent[MAX_TEXTURE_REQUESTS]; threadpoolSlots = new int[MAX_TEXTURE_REQUESTS]; // pre-configure autoreset events/download slots for (int i = 0; i < MAX_TEXTURE_REQUESTS; i++) { resetEvents[i] = new AutoResetEvent(false); threadpoolSlots[i] = -1; } } public void StartDownload() { try { GridClient.Assets.OnImageReceived += new AssetManager.ImageReceivedCallback(Assets_OnImageReceived); MainThread = new Thread(new ThreadStart(DownloadTextureThread)); MainThread.Start(); } catch (Exception er) { Logger.DebugLog(er.Message); } } //public void StopDownload() //{ //} private void DownloadTextureThread() { int reqNbr; while (true) { if (RequestQueue.Count > 0) { reqNbr = -1; // find available slot for reset event for (int i = 0; i < threadpoolSlots.Length; i++) { if (threadpoolSlots[i] == -1) { threadpoolSlots[i] = 1; reqNbr = i; break; } } if (reqNbr != -1) { UUID requestID; lock (RequestQueue) requestID = RequestQueue.Dequeue(); Logger.DebugLog(String.Format("Sending Worker thread new download request {0}", reqNbr)); ThreadPool.QueueUserWorkItem(new WaitCallback(textureRequestDoWork), new TaskInfo(requestID, reqNbr)); //gridClient.Assets.RequestImage(requestID, ImageType.Normal); continue; } } Thread.Sleep(1000); } } void textureRequestDoWork(Object threadContext) { TaskInfo ti = (TaskInfo)threadContext; lock (CurrentRequests) { if (CurrentRequests.ContainsKey(ti.RequestID)) { threadpoolSlots[ti.RequestNbr] = -1; return; } else { CurrentRequests.Add(ti.RequestID, ti.RequestNbr); } } Logger.DebugLog(String.Format("Worker {0} Requesting {1}", ti.RequestNbr, ti.RequestID)); resetEvents[ti.RequestNbr].Reset(); GridClient.Assets.RequestImage(ti.RequestID, ImageType.Normal); // don't release this worker slot until texture is downloaded or timeout occurs if (!resetEvents[ti.RequestNbr].WaitOne(300 * 1000, false)) { // Timed out Logger.Log("Worker " + ti.RequestNbr + " Timeout waiting for Texture " + ti.RequestID + " to Download", Helpers.LogLevel.Warning); lock (CurrentRequests) CurrentRequests.Remove(ti.RequestID); } // free up this download slot threadpoolSlots[ti.RequestNbr] = -1; ti = null; } private void Assets_OnImageReceived(ImageDownload image, AssetTexture asset) { if (image.Success) { //lock (CurruntTextures) //{ // if (!CurruntTextures.ContainsKey(image.ID)) // { // CurruntTextures.Add(image.ID, image.AssetData); // } // else // { // CurruntTextures.Remove(image.ID); // CurruntTextures.Add(image.ID, image.AssetData); // } //} lock(objFileAccess) if (File.Exists(Path.Combine(bmpFilePath, image.ID.ToString() + ".jpg")) == false) { SaveTexture2Image(image); } } if (onDownloadFinished != null) { onDownloadFinished(image.ID, image.Success); } } ManagedImage managedimg; Image img; Bitmap bitmap; private void SaveTexture2Image(ImageDownload image) { if (OpenJPEG.DecodeToImage(image.AssetData, out managedimg, out img)) { bitmap = new Bitmap(img); bitmap.Save(Path.Combine(bmpFilePath, image.ID.ToString() + ".jpg"), System.Drawing.Imaging.ImageFormat.Jpeg); } } public ImageDownload RetireveTextureData(UUID textureID) { ImageDownload image =null; if (gridClient.Assets.Cache.HasImage(textureID)) { image = gridClient.Assets.Cache.GetCachedImage(textureID); } else { lock (RequestQueue) { // Make sure we aren't already downloading the texture if (!RequestQueue.Contains(textureID)&& !CurrentRequests.ContainsKey(textureID)) { RequestQueue.Enqueue(textureID); } } } return image; } public void RequestTexture(UUID textureID) { //bool hasDownloaded = false; //lock (CurruntTextures) //{ // if (CurruntTextures.ContainsKey(textureID)) // { // hasDownloaded = true; // } //} //if (hasDownloaded) // return; //if (gridClient.Assets.Cache.HasImage(textureID)) //{ // // Add to rendering dictionary // lock (CurruntTextures) // { // if (!CurruntTextures.ContainsKey(textureID)) // { // CurruntTextures.Add(textureID, gridClient.Assets.Cache.GetCachedImage(textureID).AssetData); // } // } //} //else lock (objFileAccess) { if (File.Exists(Path.Combine(bmpFilePath, textureID.ToString() + ".jpg")) == false) { if (gridClient.Assets.Cache.HasImage(textureID) == false) { lock (RequestQueue) { // Make sure we aren't already downloading the texture if (!RequestQueue.Contains(textureID) && !CurrentRequests.ContainsKey(textureID)) { RequestQueue.Enqueue(textureID); } } } } } } object objFileAccess = new object(); public TextureDownloadFinished OnDownloadFinished { get { return onDownloadFinished; } set { onDownloadFinished = value; } } } class TaskInfo { public UUID RequestID; public int RequestNbr; public TaskInfo(UUID reqID, int reqNbr) { RequestID = reqID; RequestNbr = reqNbr; } } }