using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; #if UNITY_2018_3_OR_NEWER using UnityEngine.Networking; #endif /// /// Davinci - A powerful, esay-to-use image downloading and caching library for Unity in Run-Time /// v 1.2 /// Developed by ShamsDEV.com /// copyright (c) ShamsDEV.com All Rights Reserved. /// Licensed under the MIT License. /// https://github.com/shamsdev/davinci /// public class Davinci : MonoBehaviour { private static bool ENABLE_GLOBAL_LOGS = true; private bool enableLog = false; private float fadeTime = 1; private bool cached = true; private enum RendererType { none, uiImage, renderer, sprite } private RendererType rendererType = RendererType.none; private GameObject targetObj; private string url = null; private Texture2D loadingPlaceholder, errorPlaceholder; private UnityAction onStartAction, onDownloadedAction, OnLoadedAction, onEndAction; private UnityAction onDownloadProgressChange; private UnityAction onErrorAction; private static Dictionary underProcessDavincies = new Dictionary(); private string uniqueHash; private int progress; private bool success = false; static string filePath = Application.persistentDataPath + "/" + "davinci" + "/"; /// /// Get instance of davinci class /// public static Davinci get() { return new GameObject("Davinci").AddComponent(); } /// /// Set image url for download. /// /// Image Url /// public Davinci load(string url) { if (enableLog) Debug.Log("[Davinci] Url set : " + url); this.url = url; return this; } /// /// Set fading animation time. /// /// Fade animation time. Set 0 for disable fading. /// public Davinci setFadeTime(float fadeTime) { if (enableLog) Debug.Log("[Davinci] Fading time set : " + fadeTime); this.fadeTime = fadeTime; return this; } /// /// Set target Image component. /// /// target Unity UI image component /// public Davinci into(Image image) { if (enableLog) Debug.Log("[Davinci] Target as UIImage set : " + image); rendererType = RendererType.uiImage; this.targetObj = image.gameObject; return this; } /// /// Set target Renderer component. /// /// target renderer component /// public Davinci into(Renderer renderer) { if (enableLog) Debug.Log("[Davinci] Target as Renderer set : " + renderer); rendererType = RendererType.renderer; this.targetObj = renderer.gameObject; return this; } public Davinci into(SpriteRenderer spriteRenderer) { if (enableLog) Debug.Log("[Davinci] Target as SpriteRenderer set : " + spriteRenderer); rendererType = RendererType.sprite; this.targetObj = spriteRenderer.gameObject; return this; } #region Actions public Davinci withStartAction(UnityAction action) { this.onStartAction = action; if (enableLog) Debug.Log("[Davinci] On start action set : " + action); return this; } public Davinci withDownloadedAction(UnityAction action) { this.onDownloadedAction = action; if (enableLog) Debug.Log("[Davinci] On downloaded action set : " + action); return this; } public Davinci withDownloadProgressChangedAction(UnityAction action) { this.onDownloadProgressChange = action; if (enableLog) Debug.Log("[Davinci] On download progress changed action set : " + action); return this; } public Davinci withLoadedAction(UnityAction action) { this.OnLoadedAction = action; if (enableLog) Debug.Log("[Davinci] On loaded action set : " + action); return this; } public Davinci withErrorAction(UnityAction action) { this.onErrorAction = action; if (enableLog) Debug.Log("[Davinci] On error action set : " + action); return this; } public Davinci withEndAction(UnityAction action) { this.onEndAction = action; if (enableLog) Debug.Log("[Davinci] On end action set : " + action); return this; } #endregion /// /// Show or hide logs in console. /// /// 'true' for show logs in console. /// public Davinci setEnableLog(bool enableLog) { this.enableLog = enableLog; if (enableLog) Debug.Log("[Davinci] Logging enabled : " + enableLog); return this; } /// /// Set the sprite of image when davinci is downloading and loading image /// /// loading texture /// public Davinci setLoadingPlaceholder(Texture2D loadingPlaceholder) { this.loadingPlaceholder = loadingPlaceholder; if (enableLog) Debug.Log("[Davinci] Loading placeholder has been set."); return this; } /// /// Set image sprite when some error occurred during downloading or loading image /// /// error texture /// public Davinci setErrorPlaceholder(Texture2D errorPlaceholder) { this.errorPlaceholder = errorPlaceholder; if (enableLog) Debug.Log("[Davinci] Error placeholder has been set."); return this; } /// /// Enable cache /// /// public Davinci setCached(bool cached) { this.cached = cached; if (enableLog) Debug.Log("[Davinci] Cache enabled : " + cached); return this; } /// /// Start davinci process. /// public void start() { if (url == null) { error("Url has not been set. Use 'load' funtion to set image url."); return; } try { Uri uri = new Uri(url); this.url = uri.AbsoluteUri; } catch (Exception ex) { error("Url is not correct."); return; } if (rendererType == RendererType.none || targetObj == null) { error("Target has not been set. Use 'into' function to set target component."); return; } if (enableLog) Debug.Log("[Davinci] Start Working."); if (loadingPlaceholder != null) SetLoadingImage(); if (onStartAction != null) onStartAction.Invoke(); if (!Directory.Exists(filePath)) { Directory.CreateDirectory(filePath); } uniqueHash = CreateMD5(url); if (underProcessDavincies.ContainsKey(uniqueHash)) { Davinci sameProcess = underProcessDavincies[uniqueHash]; sameProcess.onDownloadedAction += () => { if (onDownloadedAction != null) onDownloadedAction.Invoke(); loadSpriteToImage(); }; } else { if (File.Exists(filePath + uniqueHash)) { if (onDownloadedAction != null) onDownloadedAction.Invoke(); loadSpriteToImage(); } else { underProcessDavincies.Add(uniqueHash, this); StopAllCoroutines(); StartCoroutine("Downloader"); } } } private IEnumerator Downloader() { if (enableLog) Debug.Log("[Davinci] Download started."); #if UNITY_2018_3_OR_NEWER UnityWebRequest www = UnityWebRequestTexture.GetTexture(url); yield return www.SendWebRequest(); #else var www = new WWW(url); #endif while (!www.isDone) { if (www.error != null) { error("Error while downloading the image : " + www.error); yield break; } #if UNITY_2018_3_OR_NEWER progress = Mathf.FloorToInt(www.downloadProgress * 100); #else progress = Mathf.FloorToInt(www.progress * 100); #endif if (onDownloadProgressChange != null) onDownloadProgressChange.Invoke(progress); if (enableLog) Debug.Log("[Davinci] Downloading progress : " + progress + "%"); yield return null; } #if UNITY_2018_3_OR_NEWER if (www.error == null) File.WriteAllBytes(filePath + uniqueHash, www.downloadHandler.data); #else if (www.error == null) File.WriteAllBytes(filePath + uniqueHash, www.bytes); #endif www.Dispose(); www = null; if (onDownloadedAction != null) onDownloadedAction.Invoke(); loadSpriteToImage(); underProcessDavincies.Remove(uniqueHash); } private void loadSpriteToImage() { progress = 100; if (onDownloadProgressChange != null) onDownloadProgressChange.Invoke(progress); if (enableLog) Debug.Log("[Davinci] Downloading progress : " + progress + "%"); if (!File.Exists(filePath + uniqueHash)) { error("Loading image file has been failed."); return; } StopAllCoroutines(); StartCoroutine(ImageLoader()); } private void SetLoadingImage() { switch (rendererType) { case RendererType.renderer: Renderer renderer = targetObj.GetComponent(); renderer.material.mainTexture = loadingPlaceholder; break; case RendererType.uiImage: Image image = targetObj.GetComponent(); Sprite sprite = Sprite.Create(loadingPlaceholder, new Rect(0, 0, loadingPlaceholder.width, loadingPlaceholder.height), new Vector2(0.5f, 0.5f)); image.sprite = sprite; break; case RendererType.sprite: SpriteRenderer spriteRenderer = targetObj.GetComponent(); Sprite spriteImage = Sprite.Create(loadingPlaceholder, new Rect(0, 0, loadingPlaceholder.width, loadingPlaceholder.height), new Vector2(0.5f, 0.5f)); spriteRenderer.sprite = spriteImage; break; } } private IEnumerator ImageLoader(Texture2D texture = null) { if (enableLog) Debug.Log("[Davinci] Start loading image."); if (texture == null) { byte[] fileData; fileData = File.ReadAllBytes(filePath + uniqueHash); texture = new Texture2D(2, 2); //ImageConversion.LoadImage(texture, fileData); texture.LoadImage(fileData); //..this will auto-resize the texture dimensions. } Color color; if (targetObj != null) switch (rendererType) { case RendererType.renderer: Renderer renderer = targetObj.GetComponent(); if (renderer == null || renderer.material == null) break; renderer.material.mainTexture = texture; float maxAlpha; if (fadeTime > 0 && renderer.material.HasProperty("_Color")) { color = renderer.material.color; maxAlpha = color.a; color.a = 0; renderer.material.color = color; float time = Time.time; while (color.a < maxAlpha) { color.a = Mathf.Lerp(0, maxAlpha, (Time.time - time) / fadeTime); if (renderer != null) renderer.material.color = color; yield return null; } } break; case RendererType.uiImage: Image image = targetObj.GetComponent(); if (image == null) break; Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f)); image.sprite = sprite; color = image.color; maxAlpha = color.a; if (fadeTime > 0) { color.a = 0; image.color = color; float time = Time.time; while (color.a < maxAlpha) { color.a = Mathf.Lerp(0, maxAlpha, (Time.time - time) / fadeTime); if (image != null) image.color = color; yield return null; } } break; case RendererType.sprite: SpriteRenderer spriteRenderer = targetObj.GetComponent(); if (spriteRenderer == null) break; Sprite spriteImage = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f)); spriteRenderer.sprite = spriteImage; color = spriteRenderer.color; maxAlpha = color.a; if (fadeTime > 0) { color.a = 0; spriteRenderer.color = color; float time = Time.time; while (color.a < maxAlpha) { color.a = Mathf.Lerp(0, maxAlpha, (Time.time - time) / fadeTime); if (spriteRenderer != null) spriteRenderer.color = color; yield return null; } } break; } if (OnLoadedAction != null) OnLoadedAction.Invoke(); if (enableLog) Debug.Log("[Davinci] Image has been loaded."); success = true; finish(); } public static string CreateMD5(string input) { // Use input string to calculate MD5 hash using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create()) { byte[] inputBytes = Encoding.ASCII.GetBytes(input); byte[] hashBytes = md5.ComputeHash(inputBytes); // Convert the byte array to hexadecimal string StringBuilder sb = new StringBuilder(); for (int i = 0; i < hashBytes.Length; i++) { sb.Append(hashBytes[i].ToString("X2")); } return sb.ToString(); } } private void error(string message) { success = false; if (enableLog) Debug.LogError("[Davinci] Error : " + message); if (onErrorAction != null) onErrorAction.Invoke(message); if (errorPlaceholder != null) StartCoroutine(ImageLoader(errorPlaceholder)); else finish(); } private void finish() { if (enableLog) Debug.Log("[Davinci] Operation has been finished."); if (!cached) { try { File.Delete(filePath + uniqueHash); } catch (Exception ex) { if (enableLog) Debug.LogError($"[Davinci] Error while removing cached file: {ex.Message}"); } } if (onEndAction != null) onEndAction.Invoke(); Invoke("destroyer", 0.5f); } private void destroyer() { Destroy(gameObject); } /// /// Clear a certain cached file with its url /// /// Cached file url. /// public static void ClearCache(string url) { try { File.Delete(filePath + CreateMD5(url)); if (ENABLE_GLOBAL_LOGS) Debug.Log($"[Davinci] Cached file has been cleared: {url}"); } catch (Exception ex) { if (ENABLE_GLOBAL_LOGS) Debug.LogError($"[Davinci] Error while removing cached file: {ex.Message}"); } } /// /// Clear all davinci cached files /// /// public static void ClearAllCachedFiles() { try { Directory.Delete(filePath, true); if (ENABLE_GLOBAL_LOGS) Debug.Log("[Davinci] All Davinci cached files has been cleared."); } catch (Exception ex) { if (ENABLE_GLOBAL_LOGS) Debug.LogError($"[Davinci] Error while removing cached file: {ex.Message}"); } } }