源战役客户端
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

653 行
20 KiB

  1. using UnityEngine;
  2. using UnityEditor;
  3. using UnityEngine.UI;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.Text;
  7. using System.IO;
  8. using System.Drawing;
  9. using System.Text.RegularExpressions;
  10. using System;
  11. public class TextureSizeChecker : EditorWindow
  12. {
  13. private static TextureSizeChecker instance;
  14. List<TextureInfo> _infoList = new List<TextureInfo>();
  15. static Texture2D _targetPic;
  16. static string _pathRoot;
  17. static float _alpha_cut_off = 0f;
  18. static int _horizontalSpace = 0;
  19. static int _verticalSpace = 0;
  20. static int _rate = 200;
  21. static int _rateImage = 200;
  22. static int _foreceHight = 23;
  23. static string splitChar = "\t";
  24. static string _ignore = "Tools,Fonts";
  25. static string[] formatOption = new string[] { "CSV", "xls","txt" };
  26. static int indexOfFormat;
  27. static string[] sortOption = new string[] { "尺寸最大值", "文件体积"};
  28. static int indexOfSort;
  29. static bool _isSotrUp;
  30. static SaveType _saveType = SaveType.csv;
  31. static bool _ignore_effect = false;
  32. static Encoding ENCODING = Encoding.GetEncoding("GB2312");
  33. static string titleStr;
  34. static bool _isCheckEmpty = true;
  35. static string[] _ignore_list;
  36. [MenuItem("Tools/检查图片")]
  37. static void ShowExcelTools()
  38. {
  39. Init();
  40. instance.Show();
  41. }
  42. enum SaveType
  43. {
  44. csv=0,
  45. xls,
  46. txt,
  47. }
  48. void OnGUI()
  49. {
  50. DrawOptions();
  51. }
  52. static bool Get2Flag(int num)
  53. {
  54. if (num < 1) return false;
  55. return (num & num - 1) == 0;
  56. }
  57. private void DrawOptions()
  58. {
  59. _pathRoot = EditorGUILayout.TextField("指定目录", _pathRoot);
  60. _ignore = EditorGUILayout.TextField("忽略目录关键字",_ignore);
  61. EditorGUILayout.HelpBox("检查的时候会忽略有上面关键字的目录,用逗号‘,’分割", MessageType.Info);
  62. _ignore_list = _ignore.Split(',');
  63. if (GUILayout.Button("UI目录"))
  64. {
  65. _pathRoot = Application.dataPath + "/LuaFramework/AssetBundleRes/ui";
  66. }
  67. if (GUILayout.Button("资源目录"))
  68. {
  69. _pathRoot = Application.dataPath + "/LuaFramework/AssetBundleRes";
  70. }
  71. EditorGUILayout.Space();
  72. EditorGUILayout.Space();
  73. EditorGUILayout.Space();
  74. EditorGUILayout.Space();
  75. _isCheckEmpty = EditorGUILayout.Toggle("是否检查空白像素", _isCheckEmpty);
  76. EditorGUILayout.HelpBox("检查图片中是否有多余的空白像素并且输出更多信息\n勾选检查将会变慢",MessageType.Info);
  77. if (_isCheckEmpty)
  78. {
  79. _ignore_effect = true;
  80. _ignore_effect = EditorGUILayout.Toggle("是否忽略特效图片", _ignore_effect);
  81. EditorGUILayout.HelpBox("勾选将会在检查空白像素的时候忽略路径中带有 'effect' 的图片\n因为特效贴图有空白像素属于正常现象", MessageType.Info);
  82. }
  83. else
  84. {
  85. _ignore_effect = false;
  86. }
  87. EditorGUILayout.Space();
  88. EditorGUILayout.Space();
  89. indexOfFormat = EditorGUILayout.Popup("储存格式", indexOfFormat, formatOption);
  90. _saveType = (SaveType)indexOfFormat;
  91. if (_saveType == SaveType.csv)
  92. {
  93. splitChar = ",";
  94. }
  95. else if(_saveType == SaveType.xls)
  96. {
  97. splitChar = "\t";
  98. }
  99. else if(_saveType == SaveType.txt)
  100. {
  101. splitChar = "\r\n";
  102. }
  103. indexOfSort = EditorGUILayout.Popup("排序关键字", indexOfSort, sortOption);
  104. _isSotrUp = EditorGUILayout.Toggle("升序", _isSotrUp);
  105. if(_isSotrUp)
  106. {
  107. EditorGUILayout.HelpBox("升序排列", MessageType.Info);
  108. }
  109. else
  110. {
  111. EditorGUILayout.HelpBox("降序排列", MessageType.Info);
  112. }
  113. EditorGUILayout.Space();
  114. EditorGUILayout.Space();
  115. titleStr = "尺寸最大值" + splitChar + "尺寸"+splitChar + "尺寸大小" + splitChar + "文件体积" + splitChar + "是否有空余像素" + splitChar + "非空范围X" + splitChar + "非空范围Y" + splitChar + "特效文件NPOT"+splitChar+"路径";
  116. _rate = EditorGUILayout.IntField("查找文件速度(文件数/帧)", _rate);
  117. _rateImage = EditorGUILayout.IntField("检查文件速度(文件数/帧)", _rateImage);
  118. if (GUILayout.Button("转换"))
  119. {
  120. EditorCoroutineRunner.StartEditorCoroutine(Conver());
  121. }
  122. }
  123. class TextureInfo
  124. {
  125. public Bitmap texture;
  126. public string fileName;
  127. public Vector2 size;
  128. /// <summary>
  129. /// 文件大小,体积
  130. /// </summary>
  131. public double volume;
  132. /// <summary>
  133. /// 图片尺寸,面积
  134. /// </summary>
  135. public int area;
  136. /// <summary>
  137. /// 是否有空余的像素
  138. /// </summary>
  139. public bool hasEmptyArea;
  140. public int emptyAreaX_start;
  141. public int emptyAreaX_end;
  142. public int emptyAreaY_start;
  143. public int emptyAreaY_end;
  144. public bool isEffect;
  145. public bool POT;
  146. public int maxSize;
  147. public TextureInfo(Bitmap pic, string clearPath)
  148. {
  149. texture = pic;
  150. //fileName = clearPath.Replace("Assets", Application.dataPath);
  151. fileName = clearPath;
  152. size = new Vector2(pic.Width, pic.Height);
  153. var fileInfo = new FileInfo(fileName);
  154. volume = System.Math.Ceiling(fileInfo.Length / 1024.0);
  155. area = pic.Width * pic.Height;
  156. isEffect = clearPath.ToLower().Contains("effect");
  157. POT = Get2Flag(pic.Width) && Get2Flag(pic.Height);
  158. maxSize = Mathf.Max(pic.Height, pic.Width);
  159. }
  160. public string GetInfo()
  161. {
  162. string str = "";
  163. str += maxSize + splitChar;
  164. str += size.x + "x" + size.y + splitChar;
  165. str += area + splitChar;
  166. str += volume + " KB" + splitChar;
  167. if(_isCheckEmpty)
  168. {
  169. str += hasEmptyArea + splitChar;
  170. if (hasEmptyArea)
  171. {
  172. str += emptyAreaX_start + "~" + emptyAreaX_end + splitChar;
  173. str += emptyAreaY_start + "~" + emptyAreaY_end + splitChar;
  174. }
  175. else
  176. {
  177. str += splitChar;
  178. str += splitChar;
  179. }
  180. }
  181. else
  182. {
  183. str += splitChar;
  184. str += splitChar;
  185. str += splitChar;
  186. }
  187. if(isEffect)
  188. {
  189. str += POT + splitChar;
  190. }
  191. else
  192. {
  193. str += splitChar;
  194. }
  195. str += fileName + splitChar;
  196. //str += "\n";
  197. return str;
  198. }
  199. }
  200. int CompareDinosBySize(TextureInfo t1, TextureInfo t2)
  201. {
  202. if (indexOfSort == 0)
  203. {
  204. if (t1.area == t2.area)
  205. {
  206. //面积相同比较文件体积
  207. if(_isSotrUp)
  208. {
  209. return (int)(t1.volume - t2.volume);
  210. }
  211. else
  212. {
  213. return (int)(t2.volume - t1.volume);
  214. }
  215. }
  216. else
  217. {
  218. //尺寸大的优先
  219. float max1 = Mathf.Max(t1.size.x, t1.size.y);
  220. float max2 = Mathf.Max(t2.size.x, t2.size.y);
  221. if(_isSotrUp)
  222. {
  223. return (int)(max2 - max1);
  224. }
  225. else
  226. {
  227. return (int)(max1 - max2);
  228. }
  229. }
  230. }
  231. else if(indexOfSort == 1)
  232. {
  233. if(_isSotrUp)
  234. {
  235. return (int)(t2.volume - t1.volume);
  236. }
  237. else
  238. {
  239. return (int)(t1.volume - t2.volume);
  240. }
  241. }
  242. return 1;
  243. }
  244. bool IsIgonrePath(string path)
  245. {
  246. foreach(var flag in _ignore_list)
  247. {
  248. if(path.ToLower().Contains(flag.ToLower()))
  249. {
  250. return true;
  251. }
  252. }
  253. return false;
  254. }
  255. IEnumerator Conver()
  256. {
  257. _infoList.Clear();
  258. var files = Directory.GetFiles(_pathRoot, "*.png*", SearchOption.AllDirectories);
  259. List<string> texturePahts = new List<string>();
  260. float count = 0;
  261. foreach (var file in files)
  262. {
  263. count++;
  264. if (count % _rate == 0)
  265. {
  266. yield return 1;
  267. }
  268. if (IsIgonrePath(file))
  269. {
  270. continue;
  271. }
  272. if (file.EndsWith(".png"))
  273. {
  274. var clearPaht = file.Replace(Application.dataPath, "Assets");
  275. texturePahts.Add(clearPaht);
  276. //Debug.Log(clearPaht);
  277. EditorUtility.DisplayProgressBar("查找文件中的PNG···", file, count / files.Length);
  278. }
  279. }
  280. var files2 = Directory.GetFiles(_pathRoot, "*.jpg*", SearchOption.AllDirectories);
  281. count = 0;
  282. foreach (var file in files2)
  283. {
  284. count++;
  285. if (count % _rate == 0)
  286. {
  287. yield return 1;
  288. }
  289. if (IsIgonrePath(file))
  290. {
  291. continue;
  292. }
  293. if (file.EndsWith(".jpg"))
  294. {
  295. var clearPaht = file.Replace(Application.dataPath, "Assets");
  296. texturePahts.Add(clearPaht);
  297. //Debug.Log(clearPaht);
  298. EditorUtility.DisplayProgressBar("查找文件中的JPG···",file, count / files2.Length);
  299. }
  300. }
  301. var files3 = Directory.GetFiles(_pathRoot, "*.tga*", SearchOption.AllDirectories);
  302. count = 0;
  303. foreach (var file in files3)
  304. {
  305. count++;
  306. if (count % _rate == 0)
  307. {
  308. yield return 1;
  309. }
  310. if (IsIgonrePath(file))
  311. {
  312. continue;
  313. }
  314. if (file.EndsWith(".tga"))
  315. {
  316. var clearPaht = file.Replace(Application.dataPath, "Assets");
  317. texturePahts.Add(clearPaht);
  318. //Debug.Log(clearPaht);
  319. EditorUtility.DisplayProgressBar("查找文件中的TGA···", file, count / files3.Length);
  320. }
  321. }
  322. List<string> error_list = new List<string>();
  323. List<string> warning_list = new List<string>();
  324. count = 0;
  325. foreach (var singPath in texturePahts)
  326. {
  327. count++;
  328. var texture = AssetDatabase.LoadAssetAtPath<Texture2D>(singPath);
  329. if (texture)
  330. {
  331. try
  332. {
  333. Bitmap pic;
  334. if (singPath.Contains(".tga"))
  335. {
  336. ImageTGA tga = new ImageTGA(singPath);
  337. if(tga.Copy_Error)
  338. {
  339. warning_list.Add(singPath);
  340. }
  341. pic = new Bitmap(tga.Image);
  342. }
  343. else
  344. {
  345. pic = Bitmap.FromFile(singPath) as Bitmap;
  346. }
  347. CheckSingleTexture(pic, singPath);
  348. pic.Dispose();
  349. }
  350. catch (Exception exp)
  351. {
  352. error_list.Add(singPath);
  353. Debug.LogError(singPath + "\n\n" + exp);
  354. }
  355. //var bs = t.EncodeToPNG();
  356. //string lastName = singlePaht.Substring(singlePaht.LastIndexOf("\\"));
  357. //Debug.Log(lastName);
  358. //File.WriteAllBytes(newDir + lastName, bs);
  359. }
  360. if (count % _rateImage == 0)
  361. {
  362. yield return 1;
  363. }
  364. EditorUtility.DisplayProgressBar("检查图片中···(共"+texturePahts.Count+"个)"+count+"/"+texturePahts.Count, singPath, count / texturePahts.Count);
  365. }
  366. _infoList.Sort(CompareDinosBySize);
  367. count = 0;
  368. List<string> str = new List<string>();
  369. str.Add(titleStr);
  370. foreach (var info in _infoList)
  371. {
  372. count++;
  373. str.Add(info.GetInfo());
  374. if (count % _rateImage*30 == 0)
  375. {
  376. yield return 1;
  377. }
  378. EditorUtility.DisplayProgressBar("整理文件中···", info.fileName, count / _infoList.Count);
  379. }
  380. str.Add("以下文件出错");
  381. str.AddRange(error_list);
  382. str.Add("以下文件读取时候出现异常,可能出错");
  383. str.AddRange(warning_list);
  384. EditorUtility.DisplayProgressBar("写入文件中···", "", 0);
  385. string newDir = Application.dataPath.Replace("Assets", "") + "/image_output";
  386. if(_saveType == SaveType.csv)
  387. {
  388. newDir += ".csv";
  389. }
  390. else if(_saveType == SaveType.xls)
  391. {
  392. newDir += ".xls";
  393. }
  394. else if(_saveType == SaveType.txt)
  395. {
  396. newDir += ".txt";
  397. }
  398. if (_infoList.Count > 0)
  399. {
  400. try
  401. {
  402. File.WriteAllLines(newDir, str.ToArray(), ENCODING);
  403. }
  404. catch(IOException exp)
  405. {
  406. EditorUtility.ClearProgressBar();
  407. EditorUtility.DisplayDialog("提示", "错误,请先关闭写入目录的文件 ", "好的呢", "");
  408. yield break;
  409. }
  410. }
  411. EditorUtility.DisplayDialog("提示", "扫描完成", "GOOD", "");
  412. EditorUtility.ClearProgressBar();
  413. AssetDatabase.Refresh();
  414. }
  415. void CheckSingleTexture(Bitmap pic, string path)
  416. {
  417. TextureInfo info = new TextureInfo(pic, path);
  418. _infoList.Add(info);
  419. if (!_isCheckEmpty)
  420. {
  421. return;
  422. }
  423. if(_ignore_effect)
  424. {
  425. if(path.ToLower().Contains("effect"))
  426. {
  427. return;
  428. }
  429. }
  430. Vector2 range_x = new Vector2(1, pic.Width);
  431. Vector2 range_y = new Vector2(1, pic.Height);
  432. bool hasEmpty = false;
  433. //Debug.Log("图片尺寸 " + pic.Width + "," + pic.Width);
  434. bool done = false;
  435. //横轴扫描 从左到右
  436. for (int i = 1; i < pic.Width; i++)
  437. {
  438. if (done)
  439. {
  440. break;
  441. }
  442. for (int j = 1; j < pic.Height; j++)
  443. {
  444. try
  445. {
  446. System.Drawing.Color col = pic.GetPixel(i, j);
  447. // data[i, j] = col;
  448. if (col.A > _alpha_cut_off)
  449. {
  450. range_x.x = i;
  451. //Debug.LogFormat(" 从左到右点 {0},{1},{2}", i, j, col);
  452. hasEmpty = true;
  453. done = true;
  454. break;
  455. }
  456. }
  457. catch
  458. {
  459. Debug.LogErrorFormat("1: {0},{1}", i, j);
  460. }
  461. }
  462. }
  463. done = false;
  464. //横轴扫描 从右到左
  465. for (int i = pic.Width - 1; i > 0; i--)
  466. {
  467. if (done)
  468. {
  469. break;
  470. }
  471. for (int j = 1; j < pic.Height; j++)
  472. {
  473. try
  474. {
  475. System.Drawing.Color col = pic.GetPixel(i, j);
  476. // data[i, j] = col;
  477. if (col.A > _alpha_cut_off)
  478. {
  479. range_x.y = i + 1;
  480. //Debug.LogFormat(" 从右到左点 {0},{1}", i, j);
  481. hasEmpty = true;
  482. done = true;
  483. break;
  484. }
  485. }
  486. catch
  487. {
  488. Debug.LogErrorFormat("2: {0},{1}", i, j);
  489. }
  490. }
  491. }
  492. done = false;
  493. //纵轴扫描 从下到上
  494. for (int i = 1; i < pic.Height; i++)
  495. {
  496. if (done)
  497. {
  498. break;
  499. }
  500. for (int j = 1; j < pic.Width; j++)
  501. {
  502. try
  503. {
  504. System.Drawing.Color col = pic.GetPixel(j, i);
  505. // data[i, j] = col;
  506. if (col.A > _alpha_cut_off)
  507. {
  508. range_y.x = i;
  509. //Debug.LogFormat(" 从下到上点 {0},{1}", j, i);
  510. hasEmpty = true;
  511. done = true;
  512. break;
  513. }
  514. }
  515. catch
  516. {
  517. Debug.LogErrorFormat("3: {0},{1}", i, j);
  518. }
  519. }
  520. }
  521. done = false;
  522. //纵轴扫描 从上到下
  523. for (int i = pic.Height - 1; i > 0; i--)
  524. {
  525. if (done)
  526. {
  527. break;
  528. }
  529. for (int j = 1; j < pic.Width; j++)
  530. {
  531. try
  532. {
  533. System.Drawing.Color col = pic.GetPixel(j, i);
  534. // data[i, j] = col;
  535. if (col.A > _alpha_cut_off)
  536. {
  537. range_y.y = i + 1;
  538. //Debug.LogFormat(" 从上到下点 {0},{1}", j, i);
  539. hasEmpty = true;
  540. done = true;
  541. break;
  542. }
  543. }
  544. catch
  545. {
  546. Debug.LogErrorFormat("4: {0},{1}", i, j);
  547. }
  548. }
  549. }
  550. if (range_x.x > range_x.y)
  551. {
  552. Debug.LogError("查找错误 ");
  553. return;
  554. }
  555. if (range_y.x > range_y.y)
  556. {
  557. Debug.LogError("查找错误 ");
  558. return;
  559. }
  560. range_x.x = Mathf.Clamp(range_x.x, 1, pic.Width);
  561. range_x.y = Mathf.Clamp(range_x.y, 1, pic.Width);
  562. range_y.x = Mathf.Clamp(range_y.x, 1, pic.Height);
  563. range_y.y = Mathf.Clamp(range_y.y, 1, pic.Height);
  564. //Debug.Log(range_x);
  565. //Debug.Log(range_y);
  566. //Debug.LogFormat("最左边的像素:{0},最后边的像素:{1},最上像素:{2},最下的像素:{3}", range_x.x, range_x.y, range_y.x, range_y.y);
  567. info.emptyAreaX_start = (int)range_x.x;
  568. info.emptyAreaY_start = (int)range_y.x;
  569. info.emptyAreaX_end = (int)range_x.y;
  570. info.emptyAreaY_end = (int)range_y.y;
  571. if(range_x.x > 1 || range_x.y < pic.Width)
  572. {
  573. info.hasEmptyArea = true;
  574. }
  575. else if(range_y.x > 1 || range_y.y < pic.Height)
  576. {
  577. info.hasEmptyArea = true;
  578. }
  579. }
  580. void OnSelectionChange()
  581. {
  582. //object[] selection = (object[])Selection.objects;
  583. //Debug.Log(selection .Length);
  584. ////判断是否有对象被选中
  585. //if (selection.Length != 1)
  586. // return;
  587. ////遍历每一个对象判断不是文件夹
  588. //string objPath = AssetDatabase.GetAssetPath((Object)selection[0]);
  589. //Debug.Log(objPath);
  590. //if( Directory.Exists(objPath) )
  591. //{
  592. // _pathRoot = objPath;
  593. // Repaint();
  594. //}
  595. //else
  596. //{
  597. //}
  598. }
  599. private static void Init()
  600. {
  601. _targetPic = null;
  602. instance = EditorWindow.GetWindow<TextureSizeChecker>("选项");
  603. _pathRoot = Application.dataPath + "/LuaFramework/AssetBundleRes";
  604. instance.position = new Rect(new Vector2(604, 290), new Vector2(650, 400));
  605. }
  606. }