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;
}
}