using UnityEngine; using UnityEditor; using UnityEngine.UI; using System.Collections; using System.Collections.Generic; using System.Text; using System.IO; using System.Drawing; using System.Text.RegularExpressions; using System; public class TextureSizeChecker : EditorWindow { private static TextureSizeChecker instance; List _infoList = new List(); static Texture2D _targetPic; static string _pathRoot; static float _alpha_cut_off = 0f; static int _horizontalSpace = 0; static int _verticalSpace = 0; static int _rate = 200; static int _rateImage = 200; static int _foreceHight = 23; static string splitChar = "\t"; static string _ignore = "Tools,Fonts"; static string[] formatOption = new string[] { "CSV", "xls","txt" }; static int indexOfFormat; static string[] sortOption = new string[] { "尺寸最大值", "文件体积"}; static int indexOfSort; static bool _isSotrUp; static SaveType _saveType = SaveType.csv; static bool _ignore_effect = false; static Encoding ENCODING = Encoding.GetEncoding("GB2312"); static string titleStr; static bool _isCheckEmpty = true; static string[] _ignore_list; [MenuItem("Tools/检查图片")] static void ShowExcelTools() { Init(); instance.Show(); } enum SaveType { csv=0, xls, txt, } void OnGUI() { DrawOptions(); } static bool Get2Flag(int num) { if (num < 1) return false; return (num & num - 1) == 0; } private void DrawOptions() { _pathRoot = EditorGUILayout.TextField("指定目录", _pathRoot); _ignore = EditorGUILayout.TextField("忽略目录关键字",_ignore); EditorGUILayout.HelpBox("检查的时候会忽略有上面关键字的目录,用逗号‘,’分割", MessageType.Info); _ignore_list = _ignore.Split(','); if (GUILayout.Button("UI目录")) { _pathRoot = Application.dataPath + "/LuaFramework/AssetBundleRes/ui"; } if (GUILayout.Button("资源目录")) { _pathRoot = Application.dataPath + "/LuaFramework/AssetBundleRes"; } EditorGUILayout.Space(); EditorGUILayout.Space(); EditorGUILayout.Space(); EditorGUILayout.Space(); _isCheckEmpty = EditorGUILayout.Toggle("是否检查空白像素", _isCheckEmpty); EditorGUILayout.HelpBox("检查图片中是否有多余的空白像素并且输出更多信息\n勾选检查将会变慢",MessageType.Info); if (_isCheckEmpty) { _ignore_effect = true; _ignore_effect = EditorGUILayout.Toggle("是否忽略特效图片", _ignore_effect); EditorGUILayout.HelpBox("勾选将会在检查空白像素的时候忽略路径中带有 'effect' 的图片\n因为特效贴图有空白像素属于正常现象", MessageType.Info); } else { _ignore_effect = false; } EditorGUILayout.Space(); EditorGUILayout.Space(); indexOfFormat = EditorGUILayout.Popup("储存格式", indexOfFormat, formatOption); _saveType = (SaveType)indexOfFormat; if (_saveType == SaveType.csv) { splitChar = ","; } else if(_saveType == SaveType.xls) { splitChar = "\t"; } else if(_saveType == SaveType.txt) { splitChar = "\r\n"; } indexOfSort = EditorGUILayout.Popup("排序关键字", indexOfSort, sortOption); _isSotrUp = EditorGUILayout.Toggle("升序", _isSotrUp); if(_isSotrUp) { EditorGUILayout.HelpBox("升序排列", MessageType.Info); } else { EditorGUILayout.HelpBox("降序排列", MessageType.Info); } EditorGUILayout.Space(); EditorGUILayout.Space(); titleStr = "尺寸最大值" + splitChar + "尺寸"+splitChar + "尺寸大小" + splitChar + "文件体积" + splitChar + "是否有空余像素" + splitChar + "非空范围X" + splitChar + "非空范围Y" + splitChar + "特效文件NPOT"+splitChar+"路径"; _rate = EditorGUILayout.IntField("查找文件速度(文件数/帧)", _rate); _rateImage = EditorGUILayout.IntField("检查文件速度(文件数/帧)", _rateImage); if (GUILayout.Button("转换")) { EditorCoroutineRunner.StartEditorCoroutine(Conver()); } } class TextureInfo { public Bitmap texture; public string fileName; public Vector2 size; /// /// 文件大小,体积 /// public double volume; /// /// 图片尺寸,面积 /// public int area; /// /// 是否有空余的像素 /// public bool hasEmptyArea; public int emptyAreaX_start; public int emptyAreaX_end; public int emptyAreaY_start; public int emptyAreaY_end; public bool isEffect; public bool POT; public int maxSize; public TextureInfo(Bitmap pic, string clearPath) { texture = pic; //fileName = clearPath.Replace("Assets", Application.dataPath); fileName = clearPath; size = new Vector2(pic.Width, pic.Height); var fileInfo = new FileInfo(fileName); volume = System.Math.Ceiling(fileInfo.Length / 1024.0); area = pic.Width * pic.Height; isEffect = clearPath.ToLower().Contains("effect"); POT = Get2Flag(pic.Width) && Get2Flag(pic.Height); maxSize = Mathf.Max(pic.Height, pic.Width); } public string GetInfo() { string str = ""; str += maxSize + splitChar; str += size.x + "x" + size.y + splitChar; str += area + splitChar; str += volume + " KB" + splitChar; if(_isCheckEmpty) { str += hasEmptyArea + splitChar; if (hasEmptyArea) { str += emptyAreaX_start + "~" + emptyAreaX_end + splitChar; str += emptyAreaY_start + "~" + emptyAreaY_end + splitChar; } else { str += splitChar; str += splitChar; } } else { str += splitChar; str += splitChar; str += splitChar; } if(isEffect) { str += POT + splitChar; } else { str += splitChar; } str += fileName + splitChar; //str += "\n"; return str; } } int CompareDinosBySize(TextureInfo t1, TextureInfo t2) { if (indexOfSort == 0) { if (t1.area == t2.area) { //面积相同比较文件体积 if(_isSotrUp) { return (int)(t1.volume - t2.volume); } else { return (int)(t2.volume - t1.volume); } } else { //尺寸大的优先 float max1 = Mathf.Max(t1.size.x, t1.size.y); float max2 = Mathf.Max(t2.size.x, t2.size.y); if(_isSotrUp) { return (int)(max2 - max1); } else { return (int)(max1 - max2); } } } else if(indexOfSort == 1) { if(_isSotrUp) { return (int)(t2.volume - t1.volume); } else { return (int)(t1.volume - t2.volume); } } return 1; } bool IsIgonrePath(string path) { foreach(var flag in _ignore_list) { if(path.ToLower().Contains(flag.ToLower())) { return true; } } return false; } IEnumerator Conver() { _infoList.Clear(); var files = Directory.GetFiles(_pathRoot, "*.png*", SearchOption.AllDirectories); List texturePahts = new List(); float count = 0; foreach (var file in files) { count++; if (count % _rate == 0) { yield return 1; } if (IsIgonrePath(file)) { continue; } if (file.EndsWith(".png")) { var clearPaht = file.Replace(Application.dataPath, "Assets"); texturePahts.Add(clearPaht); //Debug.Log(clearPaht); EditorUtility.DisplayProgressBar("查找文件中的PNG···", file, count / files.Length); } } var files2 = Directory.GetFiles(_pathRoot, "*.jpg*", SearchOption.AllDirectories); count = 0; foreach (var file in files2) { count++; if (count % _rate == 0) { yield return 1; } if (IsIgonrePath(file)) { continue; } if (file.EndsWith(".jpg")) { var clearPaht = file.Replace(Application.dataPath, "Assets"); texturePahts.Add(clearPaht); //Debug.Log(clearPaht); EditorUtility.DisplayProgressBar("查找文件中的JPG···",file, count / files2.Length); } } var files3 = Directory.GetFiles(_pathRoot, "*.tga*", SearchOption.AllDirectories); count = 0; foreach (var file in files3) { count++; if (count % _rate == 0) { yield return 1; } if (IsIgonrePath(file)) { continue; } if (file.EndsWith(".tga")) { var clearPaht = file.Replace(Application.dataPath, "Assets"); texturePahts.Add(clearPaht); //Debug.Log(clearPaht); EditorUtility.DisplayProgressBar("查找文件中的TGA···", file, count / files3.Length); } } List error_list = new List(); List warning_list = new List(); count = 0; foreach (var singPath in texturePahts) { count++; var texture = AssetDatabase.LoadAssetAtPath(singPath); if (texture) { try { Bitmap pic; if (singPath.Contains(".tga")) { ImageTGA tga = new ImageTGA(singPath); if(tga.Copy_Error) { warning_list.Add(singPath); } pic = new Bitmap(tga.Image); } else { pic = Bitmap.FromFile(singPath) as Bitmap; } CheckSingleTexture(pic, singPath); pic.Dispose(); } catch (Exception exp) { error_list.Add(singPath); Debug.LogError(singPath + "\n\n" + exp); } //var bs = t.EncodeToPNG(); //string lastName = singlePaht.Substring(singlePaht.LastIndexOf("\\")); //Debug.Log(lastName); //File.WriteAllBytes(newDir + lastName, bs); } if (count % _rateImage == 0) { yield return 1; } EditorUtility.DisplayProgressBar("检查图片中···(共"+texturePahts.Count+"个)"+count+"/"+texturePahts.Count, singPath, count / texturePahts.Count); } _infoList.Sort(CompareDinosBySize); count = 0; List str = new List(); str.Add(titleStr); foreach (var info in _infoList) { count++; str.Add(info.GetInfo()); if (count % _rateImage*30 == 0) { yield return 1; } EditorUtility.DisplayProgressBar("整理文件中···", info.fileName, count / _infoList.Count); } str.Add("以下文件出错"); str.AddRange(error_list); str.Add("以下文件读取时候出现异常,可能出错"); str.AddRange(warning_list); EditorUtility.DisplayProgressBar("写入文件中···", "", 0); string newDir = Application.dataPath.Replace("Assets", "") + "/image_output"; if(_saveType == SaveType.csv) { newDir += ".csv"; } else if(_saveType == SaveType.xls) { newDir += ".xls"; } else if(_saveType == SaveType.txt) { newDir += ".txt"; } if (_infoList.Count > 0) { try { File.WriteAllLines(newDir, str.ToArray(), ENCODING); } catch(IOException exp) { EditorUtility.ClearProgressBar(); EditorUtility.DisplayDialog("提示", "错误,请先关闭写入目录的文件 ", "好的呢", ""); yield break; } } EditorUtility.DisplayDialog("提示", "扫描完成", "GOOD", ""); EditorUtility.ClearProgressBar(); AssetDatabase.Refresh(); } void CheckSingleTexture(Bitmap pic, string path) { TextureInfo info = new TextureInfo(pic, path); _infoList.Add(info); if (!_isCheckEmpty) { return; } if(_ignore_effect) { if(path.ToLower().Contains("effect")) { return; } } Vector2 range_x = new Vector2(1, pic.Width); Vector2 range_y = new Vector2(1, pic.Height); bool hasEmpty = false; //Debug.Log("图片尺寸 " + pic.Width + "," + pic.Width); bool done = false; //横轴扫描 从左到右 for (int i = 1; i < pic.Width; i++) { if (done) { break; } for (int j = 1; j < pic.Height; j++) { try { System.Drawing.Color col = pic.GetPixel(i, j); // data[i, j] = col; if (col.A > _alpha_cut_off) { range_x.x = i; //Debug.LogFormat(" 从左到右点 {0},{1},{2}", i, j, col); hasEmpty = true; done = true; break; } } catch { Debug.LogErrorFormat("1: {0},{1}", i, j); } } } done = false; //横轴扫描 从右到左 for (int i = pic.Width - 1; i > 0; i--) { if (done) { break; } for (int j = 1; j < pic.Height; j++) { try { System.Drawing.Color col = pic.GetPixel(i, j); // data[i, j] = col; if (col.A > _alpha_cut_off) { range_x.y = i + 1; //Debug.LogFormat(" 从右到左点 {0},{1}", i, j); hasEmpty = true; done = true; break; } } catch { Debug.LogErrorFormat("2: {0},{1}", i, j); } } } done = false; //纵轴扫描 从下到上 for (int i = 1; i < pic.Height; i++) { if (done) { break; } for (int j = 1; j < pic.Width; j++) { try { System.Drawing.Color col = pic.GetPixel(j, i); // data[i, j] = col; if (col.A > _alpha_cut_off) { range_y.x = i; //Debug.LogFormat(" 从下到上点 {0},{1}", j, i); hasEmpty = true; done = true; break; } } catch { Debug.LogErrorFormat("3: {0},{1}", i, j); } } } done = false; //纵轴扫描 从上到下 for (int i = pic.Height - 1; i > 0; i--) { if (done) { break; } for (int j = 1; j < pic.Width; j++) { try { System.Drawing.Color col = pic.GetPixel(j, i); // data[i, j] = col; if (col.A > _alpha_cut_off) { range_y.y = i + 1; //Debug.LogFormat(" 从上到下点 {0},{1}", j, i); hasEmpty = true; done = true; break; } } catch { Debug.LogErrorFormat("4: {0},{1}", i, j); } } } if (range_x.x > range_x.y) { Debug.LogError("查找错误 "); return; } if (range_y.x > range_y.y) { Debug.LogError("查找错误 "); return; } range_x.x = Mathf.Clamp(range_x.x, 1, pic.Width); range_x.y = Mathf.Clamp(range_x.y, 1, pic.Width); range_y.x = Mathf.Clamp(range_y.x, 1, pic.Height); range_y.y = Mathf.Clamp(range_y.y, 1, pic.Height); //Debug.Log(range_x); //Debug.Log(range_y); //Debug.LogFormat("最左边的像素:{0},最后边的像素:{1},最上像素:{2},最下的像素:{3}", range_x.x, range_x.y, range_y.x, range_y.y); info.emptyAreaX_start = (int)range_x.x; info.emptyAreaY_start = (int)range_y.x; info.emptyAreaX_end = (int)range_x.y; info.emptyAreaY_end = (int)range_y.y; if(range_x.x > 1 || range_x.y < pic.Width) { info.hasEmptyArea = true; } else if(range_y.x > 1 || range_y.y < pic.Height) { info.hasEmptyArea = true; } } void OnSelectionChange() { //object[] selection = (object[])Selection.objects; //Debug.Log(selection .Length); ////判断是否有对象被选中 //if (selection.Length != 1) // return; ////遍历每一个对象判断不是文件夹 //string objPath = AssetDatabase.GetAssetPath((Object)selection[0]); //Debug.Log(objPath); //if( Directory.Exists(objPath) ) //{ // _pathRoot = objPath; // Repaint(); //} //else //{ //} } private static void Init() { _targetPic = null; instance = EditorWindow.GetWindow("选项"); _pathRoot = Application.dataPath + "/LuaFramework/AssetBundleRes"; instance.position = new Rect(new Vector2(604, 290), new Vector2(650, 400)); } }