using UnityEngine; using UnityEditor; using UnityEngine.UI; using System.Collections; using System.Collections.Generic; using System; using System.IO; using System.Text.RegularExpressions; using System.Drawing; using System.Drawing.Imaging; public class TextureCutoff : EditorWindow { private static TextureCutoff instance; static Texture2D _targetPic; static string _pathRoot; static float _alpha_cut_off = 0; static int _horizontalSpace = 0; static int _verticalSpace = 0; static int _rate = 10; static int _rateImage = 1; static bool _isCover = true; static string[] formatOption = new string[] { "最临近点插值(有锯齿,速度快)", "双线性插值(无锯齿,速度中等)", "双立方插值(质量最高,速度慢)" }; static int _indexOfFormat; static string _outputPaht; static bool _isOutputDir; static bool _isSinglePath; static int _forceHeight = 512; static int _forceWidth = 512; static bool _autoCheck = true; static bool _auto2up; static bool _auto2dowm; static bool _isTip; static bool _isTip3; static bool _mode_pot = true; /// /// 自动裁剪模式:选择相近的尺寸 /// static bool _auto2near = true; /// /// 是否开启自动剔除透明像素 /// static bool _isCutOffImg; static int _testInt; [MenuItem("Tools/裁剪图片")] static void ShowExcelTools() { Init(); instance.Show(); } void OnGUI() { DrawOptions(); } private void DrawOptions() { _isSinglePath = EditorGUILayout.Toggle("是否处理单个文件", _isSinglePath); if (_isSinglePath) { _targetPic = EditorGUILayout.ObjectField("指定单个文件", _targetPic, typeof(Texture2D), false) as Texture2D; _pathRoot = AssetDatabase.GetAssetPath(_targetPic).Replace("Assets", Application.dataPath); if (GUILayout.Button("获取文件夹目录")) { _pathRoot = _pathRoot.Substring(0, _pathRoot.LastIndexOf("/")); _isSinglePath = false; } } _pathRoot = EditorGUILayout.TextField("指定目录", _pathRoot); if (_isSinglePath) { EditorGUILayout.HelpBox("处理 单个文件 模式", MessageType.Info, true); } else { EditorGUILayout.HelpBox("处理 批量文件夹 模式", MessageType.Info, true); } EditorGUILayout.Space(); EditorGUILayout.Space(); EditorGUILayout.Space(); EditorGUILayout.Space(); _autoCheck = EditorGUILayout.Toggle("是否自动裁剪", _autoCheck); if (_autoCheck) { //EditorGUILayout.HorizontalScope hs = new EditorGUILayout.HorizontalScope(); //GUILayout.FlexibleSpace(); _mode_pot = EditorGUILayout.Toggle("是否2次幂", _mode_pot); if(_mode_pot) { var auto2up = EditorGUILayout.Toggle("模式:只向上", _auto2up); if (auto2up) { _auto2up = auto2up; _auto2dowm = false; _auto2near = false; } else { if (!_auto2dowm && !_auto2near) { _auto2up = true; } } var auto2dowm = EditorGUILayout.Toggle("模式:只向下", _auto2dowm); if (auto2dowm) { _auto2dowm = true; _auto2up = false; _auto2near = false; } else { if (!_auto2up && !_auto2near) { _auto2dowm = true; } } var auto2near = EditorGUILayout.Toggle("模式:尺寸接近", _auto2near); if (auto2near) { _auto2near = true; _auto2dowm = false; _auto2up = false; } else { if (!_auto2dowm && !_auto2up) { _auto2near = true; } } // hs.Dispose(); if (_auto2up) { if (_mode_pot) { EditorGUILayout.HelpBox("图片尺寸只会 向上缩放 成2的幂次", MessageType.Info, true); } else { EditorGUILayout.HelpBox("图片尺寸只会 向上缩放 成4的倍数", MessageType.Info, true); } } else if (_auto2dowm) { if (_mode_pot) { EditorGUILayout.HelpBox("图片尺寸只会 向下缩放 成2的幂次", MessageType.Info, true); } else { EditorGUILayout.HelpBox("图片尺寸只会 向下缩放 成4的倍数", MessageType.Info, true); } } else if (_auto2near) { if (_mode_pot) { EditorGUILayout.HelpBox("图片尺寸会缩放到 最接近 的2次幂", MessageType.Info, true); } else { EditorGUILayout.HelpBox("图片尺寸会缩放到 最接近 的4的倍数", MessageType.Info, true); } } } else { EditorGUILayout.HelpBox("图片尺寸会缩放到 最接近 的4的倍数", MessageType.Info, true); } } else { _forceWidth = EditorGUILayout.IntField("强制宽度", _forceWidth); _forceHeight = EditorGUILayout.IntField("强制高度", _forceHeight); EditorGUILayout.HelpBox("图片将会被强制缩放成 " + _forceHeight + " x " + _forceHeight, MessageType.Info, true); } EditorGUILayout.Space(); _indexOfFormat = EditorGUILayout.Popup("缩放算法", _indexOfFormat, formatOption); if (_indexOfFormat == 2 && !_isTip3) { if (!EditorUtility.DisplayDialog("提示", "因为算法原因,在得到最高质量的同时,透明区域会被增强,导致和原图不一致,请确认", "继续", "取消")) { _indexOfFormat = 0; } else { _isTip3 = true; } } else if (_indexOfFormat == 1 && !_isTip) { string str = "虽然使用这个算法可以得到相对无锯齿的图片,但是也会导致图片看起来相对模糊\n当前项目不推荐使用,具体原因详见文档\n当然,如果你只是想看看效果,请点击继续"; if (!EditorUtility.DisplayDialog("提示", str, "继续", "取消")) { _indexOfFormat = 0; } else { _isTip = true; } } EditorGUILayout.Space(); EditorGUILayout.Space(); EditorGUILayout.Space(); EditorGUILayout.Space(); EditorGUILayout.ToggleGroupScope tgs = new EditorGUILayout.ToggleGroupScope("是否启用裁剪无用像素", _isCutOffImg); _isCutOffImg = tgs.enabled; _alpha_cut_off = EditorGUILayout.FloatField("alpha剔除", _alpha_cut_off); EditorGUILayout.HelpBox("图片中低于这个数值的透明度将会被视为 完全透明 而被剔除", MessageType.Info, true); var space = EditorGUILayout.Vector2Field("边界扩展", new Vector2(_horizontalSpace, _verticalSpace)); _horizontalSpace = (int)space.x; _verticalSpace = (int)space.y; tgs.Dispose(); _isCover = EditorGUILayout.Toggle("是否覆盖文件", _isCover); if (_isCover) { EditorGUILayout.HelpBox("源文件将会被覆盖", MessageType.Warning, true); _isOutputDir = false; } else { _isOutputDir = EditorGUILayout.Toggle("是否合并输出在同一个目录", _isOutputDir); if (_isOutputDir) { _outputPaht = EditorGUILayout.TextField("输出目录", _outputPaht); EditorGUILayout.HelpBox("所有图片会集中输出在这个目录中", MessageType.Warning, true); } else { EditorGUILayout.HelpBox("将会输出源文件路径下 xxx_cutoff.jpg/png", MessageType.Warning, true); } } if (GUILayout.Button("转换")) { EditorCoroutineRunner.StartEditorCoroutine(Conver()); } //_testInt = EditorGUILayout.IntField("测试Int", _testInt); //if (GUILayout.Button("测试")) //{ // //Bitmap bit = new Bitmap(_pathRoot); // //ChangeTextureSize(bit, _pathRoot); // //int i = 10; // Debug.Log(Mathf.RoundToInt(0.624f)); // Debug.Log(Mathf.RoundToInt(0.424f)); // Debug.Log(Mathf.Round(0.624f)); // Debug.Log(Mathf.Round(0.424f)); // Debug.Log(Math.Round(0.624f)); // Debug.Log(Mathf.Round(0.424f)); //} } IEnumerator Conver() { List texturePahts = new List(); if (_isSinglePath) { texturePahts.Add(_pathRoot); } else { if (!Directory.Exists(_pathRoot)) { EditorUtility.DisplayDialog("错误", "目录不存在或者不是文件夹目录\n" + _pathRoot, "确定", ""); yield break; } } float count = 0; if (!_isSinglePath) //检查整个文件夹 { string[] files = new string[1]; files = Directory.GetFiles(_pathRoot, "*png*", SearchOption.AllDirectories); foreach (var file in files) { count++; if (file.EndsWith(".png")) { var clearPaht = file; texturePahts.Add(clearPaht); //Debug.Log(clearPaht); if (count % _rate == 0) { yield return 1; } EditorUtility.DisplayProgressBar("查找文件中的PNG···", "查找中 " + file, count / files.Length); } } files = Directory.GetFiles(_pathRoot, "*jpg*", SearchOption.AllDirectories); foreach (var file in files) { count++; if (file.EndsWith(".jpg")) { var clearPaht = file; texturePahts.Add(clearPaht); //Debug.Log(clearPaht); if (count % _rate == 0) { yield return 1; } EditorUtility.DisplayProgressBar("查找文件中的JPG···", "查找中 " + file, count / files.Length); } } if(_isOutputDir) { string newDir = _outputPaht; if (!Directory.Exists(newDir)) { EditorUtility.DisplayDialog("错误", "输出目录不存在\n" + _outputPaht, "确定", ""); yield break; } if (texturePahts.Count > 0) { Directory.CreateDirectory(newDir); } } } count = 0; foreach (var singlePaht in texturePahts) { count++; Bitmap prefab = new Bitmap(singlePaht); var format = prefab.RawFormat; if (prefab != null) { if (_isCutOffImg) { var clearImg = CreateNewTexture(prefab, singlePaht); if (clearImg != null) { prefab.Dispose(); prefab = clearImg; } } ChangeTextureSize(prefab, singlePaht, format); } if (count % _rateImage == 0) { yield return 1; } EditorUtility.DisplayProgressBar("创建图片中···", singlePaht, count / texturePahts.Count); } EditorUtility.ClearProgressBar(); AssetDatabase.Refresh(); } void ChangeTextureSize(Bitmap pic, string path, System.Drawing.Imaging.ImageFormat format) { int newHeight = 0; int newWidth = 0; if (_autoCheck) { if (_mode_pot) { if (pic.Width > pic.Height) //区尺寸中较大值去计算接近的POT { newWidth = GetPOT(pic.Width); newHeight = newWidth; } else { newHeight = GetPOT(pic.Height); newWidth = newHeight; } } else { newWidth = GetMOF(pic.Width); newHeight = GetMOF(pic.Height); } } else { newHeight = _forceHeight; newWidth = _forceWidth; } if(newHeight == pic.Height && newWidth == pic.Width) { return; } Bitmap img = new Bitmap(pic, newWidth, newHeight); System.Drawing.Color[,] cols = new System.Drawing.Color[pic.Width, pic.Height]; //缓存所有像素 for (int i = 0; i < pic.Width; i++) { for (int j = 0; j < pic.Height; j++) { cols[i, j] = pic.GetPixel(i, j); } } double scale_x = newWidth / (float)pic.Width; //长度缩放比 double scale_y = newHeight / (float)pic.Height; //宽度缩放比 //Debug.LogFormat("新宽高:{0} x {1}", newWidth, newHeight); if (_indexOfFormat == 2) { ////双立方差值 算法 --内置的 System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(img); g.DrawImage(img, 0, 0, newWidth, newHeight); } else { //填充新尺寸图片的每个像素 for (int i = 0; i < newWidth; i++) { for (int j = 0; j < newHeight; j++) { if (_indexOfFormat == 0) { //最近像素 算法 int sample_x = (int)Math.Round(i / scale_x, MidpointRounding.AwayFromZero); //原图中对应的X int sample_y = (int)Math.Round(j / scale_y, MidpointRounding.AwayFromZero);//原图中对应的Y sample_x = Mathf.Clamp(sample_x, 0, pic.Width - 1); sample_y = Mathf.Clamp(sample_y, 0, pic.Height - 1); try { var tempCol = cols[sample_x, sample_y]; if (tempCol.A != 0) { img.SetPixel(i, j, cols[sample_x, sample_y]); } } catch (Exception exp) { Debug.LogError(exp); Debug.LogErrorFormat("{0},{1}", i, j); } } else if (_indexOfFormat == 1) { int sample_x = 0; int sample_y = 0; System.Drawing.Color final; //双线滤波 算法 if (scale_x > 1 && scale_y < 1) { int min = (int)(i / scale_x); int max = min + 1; max = Mathf.Clamp(max, 0, pic.Width - 1); double delta = (i / scale_x) - min; sample_y = (int)Math.Round(j / scale_y);//原图中对应的Y final = LerpColor(cols[min, sample_y], cols[max, sample_y], (float)delta); } else if (scale_y > 1 && scale_x < 1) { int min = (int)(j / scale_y); int max = min + 1; max = Mathf.Clamp(max, 0, pic.Height - 1); double delta = (j / scale_y) - min; sample_x = (int)Math.Round(i / scale_x); //原图中对应的X final = LerpColor(cols[sample_x, min], cols[sample_x, max], (float)delta); } else if (scale_x > 1 && scale_y > 1) { int min_x = (int)(i / scale_x); int max_x = min_x + 1; max_x = Mathf.Clamp(max_x, 0, pic.Width - 1); double delta_x = (i / scale_x) - min_x; int min_y = (int)(j / scale_y); int max_y = min_y + 1; max_y = Mathf.Clamp(max_y, 0, pic.Height - 1); double delta_y = (j / scale_y) - min_y; var final_x = LerpColor(cols[min_x, min_y], cols[max_x, min_y], (float)delta_x); var final_y = LerpColor(cols[min_x, max_y], cols[max_x, max_y], (float)delta_x); final = LerpColor(final_x, final_y, (float)delta_y); } else { //最近像素 算法 sample_x = (int)Math.Round(i / scale_x); //原图中对应的X sample_y = (int)Math.Round(j / scale_y);//原图中对应的Y sample_x = Mathf.Clamp(sample_x, 0, pic.Width - 1); sample_y = Mathf.Clamp(sample_y, 0, pic.Height - 1); final = cols[sample_x, sample_y]; //System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(img); //g.DrawImage(img, 0, 0, 512.0f, 512.0f); } try { img.SetPixel(i, j, final); } catch (Exception exp) { Debug.LogError(exp); Debug.LogErrorFormat("{0},{1}", i, j); } } } } } string fileName; path = path.Replace("\\", "/"); //if (_isCover) //{ fileName = path.Substring(path.LastIndexOf("/")); //} //else //{ //fileName = path.Substring(path.LastIndexOf("\\")); //} //img = new Bitmap(512, 512); //for(int i = 1;i<512;i++) //{ // for(int j = 1;j<512;j++) // { // img.SetPixel(i, j, System.Drawing.Color.FromArgb(255,255,255,255)); // } //} string newDir = _outputPaht; if (_isOutputDir) //在统一目录输出 { if (!Directory.Exists(newDir)) { Directory.CreateDirectory(newDir); } newDir += fileName; } else { if (_isCover) { newDir = path; } else { newDir = path.Replace(".", "_cutoff."); } } if (_isCover) { pic.Dispose(); img.Save(newDir); } else { pic.Dispose(); img.Save(newDir); } Debug.Log(format); } System.Drawing.Color LerpColor(System.Drawing.Color col1, System.Drawing.Color col2, float delta) { var col = System.Drawing.Color.FromArgb((int)Mathf.Lerp(col1.A, col2.A, delta), (int)Mathf.Lerp(col1.R, col2.R, delta), (int)Mathf.Lerp(col1.G, col2.G, delta), (int)Mathf.Lerp(col1.B, col2.B, delta)); return col; } int GetPOT(int cur) { float check = 0; for (int i = 1; i < 12; i++) { check = Mathf.Pow(2, i); if (_auto2up) { if (check > cur) { return (int)check; } } else if (_auto2dowm) { if (check > cur) { return (int)Mathf.Pow(2, i - 1); } } else if (_auto2near) { if (check > cur) { float last = Mathf.Pow(2, i - 1); float upDelta = check - cur; float dowmDelta = cur - last; if (upDelta >= dowmDelta) { return (int)last; } else { return (int)check; } } } } return 0; } int GetMOF(int cur) { int x = cur / 4; return 4 * x; } Bitmap CreateNewTexture(Bitmap pic, string path) { if (path.EndsWith(".jpg")) //JPG没有通明通道 { return pic; } Vector2 range_x = new Vector2(0, pic.Width); Vector2 range_y = new Vector2(0, pic.Height); //Debug.Log("图片尺寸 " + pic.Width + "," + pic.Width); bool done = false; //横轴扫描 从左到右 for (int i = 0; i < pic.Width; i++) { if (done) { break; } for (int j = 0; j < pic.Height; j++) { 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); done = true; break; } } } done = false; //横轴扫描 从右到左 for (int i = pic.Width - 1; i > 0; i--) { if (done) { break; } for (int j = 0; j < pic.Height; j++) { 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}", range_x.y, j, col); done = true; break; } } } done = false; //纵轴扫描 从下到上 for (int i = 0; i < pic.Height; i++) { if (done) { break; } for (int j = 0; j < pic.Width; j++) { 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}", range_y.x, i, col); done = true; break; } } } done = false; //纵轴扫描 从上到下 for (int i = pic.Height - 1; i > 0; i--) { if (done) { break; } for (int j = 0; j < pic.Width; j++) { 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}", range_y.y, i,col); done = true; break; } } } if (range_x.x > range_x.y) { //Debug.LogError("查找错误 "); return null; } if (range_y.x > range_y.y) { //Debug.LogError("查找错误 "); return null; } range_x.x = Mathf.Clamp(range_x.x, 0, pic.Width); range_x.y = Mathf.Clamp(range_x.y, 0, pic.Width); range_y.x = Mathf.Clamp(range_y.x, 0, pic.Height); range_y.y = Mathf.Clamp(range_y.y, 0, 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); Vector2 newSize = new Vector2(range_x.y - range_x.x, range_y.y - range_y.x); Bitmap newImg = new Bitmap((int)newSize.x + _horizontalSpace * 2, (int)newSize.y + _verticalSpace * 2, pic.PixelFormat); for (var i = range_x.x; i < range_x.y; i++) { for (var j = range_y.x; j < range_y.y; j++) { System.Drawing.Color col = pic.GetPixel((int)i, (int)j); //print(col); //col = new Color(255, 0, 0, 0); //Debug.Log((i - range_x.x) + "," + (j - range_y.x)); try { newImg.SetPixel((int)(i - range_x.x) + _horizontalSpace, (int)(j - range_y.x) + _verticalSpace, col); } catch { Debug.LogErrorFormat("{0},{1}", (int)(i - range_x.x) + _horizontalSpace, (int)(j - range_y.x) + _verticalSpace); } } } //Debug.Log(newSize); //Debug.Log(newSize.x - _horizontalSpace); //Debug.Log(newSize.y - _verticalSpace); //填充空白透明像素 for (int i = 0; i < newImg.Width; i++) { for (int j = 0; j < newImg.Height; j++) { if (i < _horizontalSpace || i >= (newSize.x + _horizontalSpace)) { // Debug.Log(i + "," + j); var col = System.Drawing.Color.FromArgb(0, 0, 0, 0); newImg.SetPixel(i, j, col); } else if (j < _verticalSpace || j >= (newSize.y + _verticalSpace)) { // Debug.Log(i + ","+ j); var col = System.Drawing.Color.FromArgb(0, 0, 0, 0); newImg.SetPixel(i, j, col); } } } //string newDir = _outputPaht; //string fileName = path.Substring(path.LastIndexOf("\\")); //if (_isOutputDir) //在统一目录输出 //{ // if (!Directory.Exists(newDir)) // { // Directory.CreateDirectory(newDir); // } // newDir += fileName; //} //else //{ // if (_isCover) // { // newDir = path; // } // else // { // newDir = path.Replace(".", "_cutoff."); // } //} // Debug.Log(newDir); //newImg.Save(newDir, System.Drawing.Imaging.ImageFormat.Png); return newImg; } void OnSelectionChange() { if (!_isSinglePath) { return; } object[] selection = (object[])Selection.objects; //判断是否有对象被选中 if (selection.Length != 1) return; if (selection[0].GetType() == typeof(Texture2D)) { _targetPic = (Texture2D)selection[0]; string objPath = AssetDatabase.GetAssetPath(_targetPic); _pathRoot = objPath; Repaint(); } } private static void Init() { _targetPic = null; instance = EditorWindow.GetWindow("选项"); _pathRoot = Application.dataPath; _outputPaht = _pathRoot + "/image_output"; instance.position = new Rect(new Vector2(604, 290), new Vector2(750, 600)); _isTip = false; _indexOfFormat = 0; } }