857 lines
25 KiB
C#
Raw Normal View History

2025-04-18 19:18:15 +08:00
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.IO;
using System.Collections.Generic;
using System.Security.Policy;
using System;
using UnityEngine.Networking;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using TEngine;
using WebP;
public class AsyncImageDownload : MonoBehaviour
{
private Dictionary<int, Sprite> sprites = new Dictionary<int, Sprite>();
public Dictionary<string, Texture2D> textures = new Dictionary<string, Texture2D>();
private Sprite myWXPic;
private static AsyncImageDownload _instance = null;
public static AsyncImageDownload GetInstance() { return Instance; }
public static AsyncImageDownload Instance
{
get
{
if (_instance == null)
{
GameObject obj = new GameObject("AsyncImageDownload");
_instance = obj.AddComponent<AsyncImageDownload>();
DontDestroyOnLoad(obj);
_instance.Init();
}
return _instance;
}
}
public void CleanMemory()
{
this.sprites.Clear();
this.textures.Clear();
}
public bool Init()
{
if (!Directory.Exists(Application.persistentDataPath + "/ImageCache/"))
{
Directory.CreateDirectory(Application.persistentDataPath + "/ImageCache/");
}
myWXPic = null;
return true;
}
public static string GetBigMd5(string str)
{
string cl = str;
string pwd = "";
var md5 = MD5.Create();
byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(cl));
for (int i = 0; i < s.Length; i++)
{
pwd = pwd + s[i].ToString("X2");
}
return pwd;
}
public async Task SetAsyncImage(string url, RawImage image)
{
// 神秘人头像不缓存
if (url == "smr")
{
GameModule.Resource.LoadAsset<Texture2D>("smr", (tex) =>
{
if (image != null)
{
image.texture = tex;
}
});
return;
}
var id = GetBigMd5(url);
if (textures.ContainsKey(id))
{
if (image != null)
{
image.texture = textures[id];
}
return;
}
if (!File.Exists(path + id))
{
await DownloadImageRaw(id, url, image);
}
else
{
await LoadLocalImageRaw(id, url, image);
}
}
public async Task SetAsyncImage(string url, SpriteRenderer spriteRenderer)
{
// Check if the URL is for a special case
if (url == "smr")
{
GameModule.Resource.LoadAsset<Texture2D>("smr", (tex) =>
{
if (spriteRenderer != null)
{
spriteRenderer.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f));
}
});
return;
}
var id = GetBigMd5(url);
if (textures.ContainsKey(id))
{
if (spriteRenderer != null)
{
var tex = textures[id];
var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f));
sprite.name = id;
spriteRenderer.sprite = sprite;
}
return;
}
if (!File.Exists(path + id))
{
await DownloadImageSprite(id, url, spriteRenderer);
}
else
{
await LoadLocalImageSprite(id, url, spriteRenderer);
}
}
public async Task SetAsyncImage(string url, Image image)
{
// Check if the URL is for a special case
if (url == "smr")
{
GameModule.Resource.LoadAsset<Texture2D>("smr", (tex) =>
{
if (image != null)
{
var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f));
image.sprite = sprite;
}
});
return;
}
var id = GetBigMd5(url);
if (textures.ContainsKey(id))
{
if (image != null)
{
var tex = textures[id];
var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f));
sprite.name = id; // 设置Sprite的名称
image.sprite = sprite;
}
return;
}
if (!File.Exists(GetFilePath(id)))
{
await DownloadImageForImage(id, url, image);
}
else
{
await LoadLocalImageForImage(id, url, image);
}
}
//int index,Action<int,Texture2D> act
IEnumerator DownloadImageRaw2(string id, string url, RawImage image, int index, Action<int, string, Texture2D> act)
{
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(url))
{
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.Success)
{
//Debug.LogError(url+" "+www.result);
act?.Invoke(index, url, null);
}
else
{
Texture2D tex2d = DownloadHandlerTexture.GetContent(www); //www.texture;
if (tex2d != null)
{
if (image != null)
{
image.texture = tex2d;
}
textures[id] = tex2d;
//将图片保存至缓存路径
byte[] pngData = tex2d.EncodeToPNG();//将材质压缩成byte流
File.WriteAllBytes(path + id, pngData);
}
act?.Invoke(index, url, tex2d);
}
}
}
private async Task DownloadImageRaw(string id, string url, RawImage image)
{
using (UnityWebRequest www = UnityWebRequest.Get(url))
{
var asyncOp = www.SendWebRequest();
while (!asyncOp.isDone)
{
await Task.Yield();
}
if (www.result != UnityWebRequest.Result.Success)
{
if (image != null)
{
image.texture = null;
}
}
else
{
byte[] data = www.downloadHandler.data;
// 在解码前获取图片尺寸
var (originalWidth, originalHeight) = GetImageSize(data);
// 计算限制大小后的尺寸
var (targetWidth, targetHeight) = CalculateTargetSize(originalWidth, originalHeight, 100, 100);
// Debug.Log($"Image size before decode: {originalWidth}x{originalHeight}, target size: {targetWidth}x{targetHeight} from URL: {url}");
Texture2D tex2d = null;
if (IsWebP(data))
{
tex2d = DecodeWebP(data, targetWidth, targetHeight);
}
else
{
tex2d = new Texture2D(targetWidth, targetHeight);
tex2d.LoadImage(data);
}
if (tex2d != null)
{
if (image != null)
{
image.texture = tex2d;
}
if (!textures.ContainsKey(id))
{
textures[id] = tex2d;
}
byte[] pngData = tex2d.EncodeToPNG();
File.WriteAllBytes(path + id, pngData);
}
else
{
if (image != null)
{
image.texture = null;
}
}
}
}
}
// 计算目标尺寸,保持宽高比并限制最大尺寸
private (int width, int height) CalculateTargetSize(int originalWidth, int originalHeight, int maxWidth, int maxHeight)
{
if (originalWidth <= 0 || originalHeight <= 0)
return (2, 2); // 默认最小尺寸
if (originalWidth <= maxWidth && originalHeight <= maxHeight)
return (originalWidth, originalHeight); // 如果原始尺寸已经在限制范围内,直接使用
float ratio = Mathf.Min(
maxWidth / (float)originalWidth,
maxHeight / (float)originalHeight
);
int targetWidth = Mathf.Max(2, Mathf.RoundToInt(originalWidth * ratio));
int targetHeight = Mathf.Max(2, Mathf.RoundToInt(originalHeight * ratio));
return (targetWidth, targetHeight);
}
// 检查是否是WebP格式
private bool IsWebP(byte[] data)
{
if (data.Length < 12) return false;
// WebP文件头标识"RIFF" + 4字节 + "WEBP"
return data[0] == 'R' && data[1] == 'I' && data[2] == 'F' && data[3] == 'F'
&& data[8] == 'W' && data[9] == 'E' && data[10] == 'B' && data[11] == 'P';
}
// 使用SimpleWebP解码WebP图片
private Texture2D DecodeWebP(byte[] data, int width, int height)
{
try
{
// 使用SimpleWebP插件解码
// Texture2D texture = SimpleWebP.Load(data);
// var texutre = Texture2DExt.CreateTexture2DFromWebP(data,false,false,(e)=>{});
Texture2D texture = new Texture2D(width, height);
texture.LoadWebP(data, out Error error);
return texture;
}
catch (Exception e)
{
Debug.LogError($"WebP decode error: {e.Message}");
return null;
}
}
// 这里有很大的GC隐患概率卡顿的问题在这
IEnumerator LoadLocalImageRaw2(string id, string url, RawImage image, int index, Action<int, string, Texture2D> act)
{
string filePath = "file:///" + path + id;
//Debug.LogError(filePath);
//WWW www = new WWW(filePath);
//yield return www;
//Texture2D texture = www.texture;
//textures[id] = texture;
////image.texture = texture;
//act?.Invoke(index, url, texture);
//www.Dispose();
//string filePath = path + url.GetHashCode();
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(filePath))
{
yield return www.SendWebRequest();
Texture2D texture = DownloadHandlerTexture.GetContent(www);
textures[id] = texture;
//image.texture = texture;
act?.Invoke(index, url, texture);
}
}
// 这里有很大的GC隐患概率卡顿的问题在这
private async Task LoadLocalImageRaw(string id, string url, RawImage image)
{
string filePath = "file:///" + path + id;
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(filePath))
{
var asyncOp = www.SendWebRequest();
while (!asyncOp.isDone)
{
await Task.Yield();
}
Texture2D texture = DownloadHandlerTexture.GetContent(www);
if (!textures.ContainsKey(id))
{
textures[id] = texture;
}
if (image != null)
{
image.texture = texture;
}
}
}
public string path
{
get
{
//pc,ios //android :jar:file//
return Application.persistentDataPath + "/ImageCache/";
}
}
public async Task PreCacheImage(string url)
{
// 神秘人头像不缓存
if (url == "smr")
{
return;
}
var id = GetBigMd5(url);
if (textures.ContainsKey(id))
{
// 图像已经缓存,无需再次下载
return;
}
if (!File.Exists(path + id))
{
await DownloadImageRaw(id, url, null);
}
else
{
await LoadLocalImageRaw(id, url, null);
}
}
private (int width, int height) GetImageSize(byte[] data)
{
if (IsWebP(data))
{
return GetWebPSize(data);
}
else if (IsPNG(data))
{
return GetPNGSize(data);
}
else if (IsJPG(data))
{
return GetJPGSize(data);
}
return (0, 0);
}
private bool IsPNG(byte[] data)
{
if (data.Length < 8) return false;
return data[0] == 0x89 && data[1] == 0x50 && data[2] == 0x4E && data[3] == 0x47;
}
private (int width, int height) GetPNGSize(byte[] data)
{
if (data.Length < 24) return (0, 0);
int width = (data[16] << 24) | (data[17] << 16) | (data[18] << 8) | data[19];
int height = (data[20] << 24) | (data[21] << 16) | (data[22] << 8) | data[23];
return (width, height);
}
private bool IsJPG(byte[] data)
{
if (data.Length < 2) return false;
return data[0] == 0xFF && data[1] == 0xD8;
}
private (int width, int height) GetJPGSize(byte[] data)
{
int pos = 2;
while (pos < data.Length - 2)
{
if (data[pos] != 0xFF) return (0, 0);
if (data[pos + 1] == 0xC0 || data[pos + 1] == 0xC2)
{
int height = (data[pos + 5] << 8) | data[pos + 6];
int width = (data[pos + 7] << 8) | data[pos + 8];
return (width, height);
}
pos += 2 + ((data[pos + 2] << 8) | data[pos + 3]);
}
return (0, 0);
}
private (int width, int height) GetWebPSize(byte[] data)
{
if (data.Length < 30) return (0, 0);
// WebP VP8
if (data[12] == 'V' && data[13] == 'P' && data[14] == '8')
{
int width = (data[26] | (data[27] << 8)) & 0x3FFF;
int height = (data[28] | (data[29] << 8)) & 0x3FFF;
return (width, height);
}
return (0, 0);
}
private async Task DownloadImageSprite(string id, string url, SpriteRenderer spriteRenderer)
{
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(url))
{
var asyncOp = www.SendWebRequest();
while (!asyncOp.isDone)
{
await Task.Yield();
}
if (www.result != UnityWebRequest.Result.Success)
{
if (spriteRenderer != null)
{
spriteRenderer.sprite = null;
}
}
else
{
byte[] data = www.downloadHandler.data;
Texture2D tex2d = new Texture2D(2, 2);
tex2d.LoadImage(data);
if (tex2d != null)
{
if (spriteRenderer != null)
{
var sprite = Sprite.Create(tex2d, new Rect(0, 0, tex2d.width, tex2d.height), new Vector2(0.5f, 0.5f));
sprite.name = id;
spriteRenderer.sprite = sprite;
}
if (!textures.ContainsKey(id))
{
textures[id] = tex2d;
}
byte[] pngData = tex2d.EncodeToPNG();
File.WriteAllBytes(path + id, pngData);
}
else
{
if (spriteRenderer != null)
{
spriteRenderer.sprite = null;
}
}
}
}
}
private async Task LoadLocalImageSprite(string id, string url, SpriteRenderer spriteRenderer)
{
string filePath = "file:///" + path + id;
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(filePath))
{
var asyncOp = www.SendWebRequest();
while (!asyncOp.isDone)
{
await Task.Yield();
}
Texture2D texture = DownloadHandlerTexture.GetContent(www);
if (!textures.ContainsKey(id))
{
textures[id] = texture;
}
if (spriteRenderer != null)
{
var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
sprite.name = id;
spriteRenderer.sprite = sprite;
}
}
}
public void SetSyncImage(string url, RawImage image)
{
if (url == "smr")
{
GameModule.Resource.LoadAsset<Texture2D>("smr", (tex) =>
{
if (image != null)
{
image.texture = tex;
}
});
return;
}
var id = GetBigMd5(url);
if (textures.ContainsKey(id))
{
if (image != null)
{
image.texture = textures[id];
}
return;
}
if (!File.Exists(path + id))
{
DownloadImageRawSync(id, url, image);
}
else
{
LoadLocalImageRawSync(id, url, image);
}
}
private void DownloadImageRawSync(string id, string url, RawImage image)
{
using (UnityWebRequest www = UnityWebRequest.Get(url))
{
www.SendWebRequest();
while (!www.isDone) { }
if (www.result != UnityWebRequest.Result.Success)
{
if (image != null)
{
image.texture = null;
}
}
else
{
byte[] data = www.downloadHandler.data;
Texture2D tex2d = new Texture2D(2, 2);
tex2d.LoadImage(data);
if (tex2d != null)
{
if (image != null)
{
image.texture = tex2d;
}
if (!textures.ContainsKey(id))
{
textures[id] = tex2d;
}
byte[] pngData = tex2d.EncodeToPNG();
File.WriteAllBytes(path + id, pngData);
}
else
{
if (image != null)
{
image.texture = null;
}
}
}
}
}
private void LoadLocalImageRawSync(string id, string url, RawImage image)
{
string filePath = "file:///" + path + id;
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(filePath))
{
www.SendWebRequest();
while (!www.isDone) { }
Texture2D texture = DownloadHandlerTexture.GetContent(www);
if (!textures.ContainsKey(id))
{
textures[id] = texture;
}
if (image != null)
{
image.texture = texture;
}
}
}
public void SetSyncImage(string url, SpriteRenderer spriteRenderer)
{
if (url == "smr")
{
GameModule.Resource.LoadAsset<Texture2D>("smr", (tex) =>
{
if (spriteRenderer != null)
{
spriteRenderer.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f));
}
});
return;
}
var id = GetBigMd5(url);
if (textures.ContainsKey(id))
{
if (spriteRenderer != null)
{
var tex = textures[id];
var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f));
sprite.name = id;
spriteRenderer.sprite = sprite;
}
return;
}
if (!File.Exists(path + id))
{
DownloadImageSpriteSync(id, url, spriteRenderer);
}
else
{
LoadLocalImageSpriteSync(id, url, spriteRenderer);
}
}
private void DownloadImageSpriteSync(string id, string url, SpriteRenderer spriteRenderer)
{
using (UnityWebRequest www = UnityWebRequest.Get(url))
{
www.SendWebRequest();
while (!www.isDone) { }
if (www.result != UnityWebRequest.Result.Success)
{
if (spriteRenderer != null)
{
spriteRenderer.sprite = null;
}
}
else
{
byte[] data = www.downloadHandler.data;
Texture2D tex2d = new Texture2D(2, 2);
tex2d.LoadImage(data);
if (tex2d != null)
{
if (spriteRenderer != null)
{
var sprite = Sprite.Create(tex2d, new Rect(0, 0, tex2d.width, tex2d.height), new Vector2(0.5f, 0.5f));
sprite.name = id;
spriteRenderer.sprite = sprite;
}
if (!textures.ContainsKey(id))
{
textures[id] = tex2d;
}
byte[] pngData = tex2d.EncodeToPNG();
File.WriteAllBytes(path + id, pngData);
}
else
{
if (spriteRenderer != null)
{
spriteRenderer.sprite = null;
}
}
}
}
}
private void LoadLocalImageSpriteSync(string id, string url, SpriteRenderer spriteRenderer)
{
string filePath = "file:///" + path + id;
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(filePath))
{
www.SendWebRequest();
while (!www.isDone) { }
Texture2D texture = DownloadHandlerTexture.GetContent(www);
if (!textures.ContainsKey(id))
{
textures[id] = texture;
}
if (spriteRenderer != null)
{
var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
sprite.name = id;
spriteRenderer.sprite = sprite;
}
}
}
private async Task DownloadImageForImage(string id, string url, Image image)
{
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(url))
{
var asyncOp = www.SendWebRequest();
while (!asyncOp.isDone)
{
await Task.Yield();
}
if (www.result != UnityWebRequest.Result.Success)
{
if (image != null)
{
image.sprite = null;
}
}
else
{
byte[] data = www.downloadHandler.data;
Texture2D tex2d = new Texture2D(2, 2);
tex2d.LoadImage(data);
if (tex2d != null)
{
if (image != null)
{
var sprite = Sprite.Create(tex2d, new Rect(0, 0, tex2d.width, tex2d.height), new Vector2(0.5f, 0.5f));
sprite.name = id; // 设置Sprite的名称
image.sprite = sprite;
}
if (!textures.ContainsKey(id))
{
textures[id] = tex2d;
}
byte[] pngData = tex2d.EncodeToPNG();
File.WriteAllBytes(GetFilePath(id), pngData);
}
else
{
if (image != null)
{
image.sprite = null;
}
}
}
}
}
private async Task LoadLocalImageForImage(string id, string url, Image image)
{
string filePath = "file:///" + GetFilePath(id);
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(filePath))
{
var asyncOp = www.SendWebRequest();
while (!asyncOp.isDone)
{
await Task.Yield();
}
Texture2D texture = DownloadHandlerTexture.GetContent(www);
if (!textures.ContainsKey(id))
{
textures[id] = texture;
}
if (image != null)
{
var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
sprite.name = id; // 设置Sprite的名称
image.sprite = sprite;
}
}
}
private string GetFilePath(string id)
{
return path + id;
}
}