赞
踩
不知道有没有人遇到过这种情况。
资源大小并非2的整数幂,导致Unity无法压缩。
而且资源量很大,无法放在PS里去一张一张的去修改画布。
最开始,我是放在PS里修改画布来让图片修改为2的整数幂的尺寸。
但是看着文件夹里几百帧的贴图。
我顿时陷入了沉思。
显然不能手动来做…
Unity的Texture2D似乎可以用来处理这个问题。
需要注意的一个地方
Color[] texturePixels = Texture2D.GetPixels();
我原本以为会是一个二维数组,因为图片像素的排布可以看作一个二维数组么。
不过问题也不大
像素图片的像素在数组中的索引是以这种方式排布的。
所以如何把一张图的像素转到另一张上,便成了这样的一个数学问题。
(需要创建一张目标规格的图片,然后将原图的像素转移到正确位置)
(左:目标图,右:原图)
所以实际在索引并且确定目标图每一个像素颜色的时候,需要将目标图的索引号(左)转换为原图的索引号(右)并正确赋值,才能够将原图的信息正确的保存在我们目标规格的图片上。
实际上也只是一个
0 = func(7)
1 = func(8)
…
4 = func(13)
…
的问题。
在Texture的定义里,是有图片的宽高的,通过宽高,我们便可以将这个一维数组转换为一个二维数组的模型,来获得这个像素所在的行和列(个人row,culomn容易搞混,所以行列用x,y表示)
Texture2D.width
Texture2D.height
首先还是分析个例,
在左图上7号像素所在的位置是(1,1),对应的像素是右图中0号像素(0,0)。
从(1,1)到(0,0),x,y都减掉了1。
1是两张图片的长宽差的一半。
所以就有了(伪代码)
(current代表原图,target代表目标图,类型Texture2D)
public int GetCurrentIndex(int targetIndex) { int widthDifference = target.width - current.width; int heightDifference = target.height - current.height; widthDifference /= 2; heightDifference /= 2; int x = targetIndex % target.width; int y = targetIndex / target.width; x -= widthDifference; y -= heightDifference; int currentIndex = y * current.width + x; return currentIndex; }
可以通过先将目标图片得一维索引转换为二维索引,减去两张图片相差得结果之后,获得在原图得二维索引,再转换为原图得一维索引,这样就可以获得原图在目标图上每个像素得正确位置。
也就是完成了 0 = func(7)的 func 函数算法。
但是这样做的话,不仅目标图蓝圈内的像素会获取像素,外部的像素依旧能够找到,而且也可能因为太大而超出原图的索引。所以我们需要做一个判断,来让外部的像素完全透明。
(同样是伪代码)
if(x >= 0 && y >= 0 && x < current.width && y < current.height && currentIndex< currentPixels.Length)
{
targetPixels[targetIndex] = currentPixels[currentIndex];
}
else
{
targetPixels[targetIndex] = new Color(0, 0, 0, 0);
}
只有符合全部条件(在篮圈内部)的像素才会被赋值。
上面大多是讲原理并非实际代码,可能很多地方看起来会脑壳疼。
下面是编辑器的完整代码,放在Editor目录下即可使用。
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using System.IO; public class TrimCanvasEditor : EditorWindow { [MenuItem("ImageHandle/TrimCanvas")] private static void GetWindow() { TrimCanvasEditor window = GetWindow<TrimCanvasEditor>(true, "修改画布大小", true); window.Show(); } private SerializedObject serializedObject; private SerializedProperty texture2DListProperty; private Vector2Int trimSize; private Vector2Int offset; private string savePath; private string namePrefix; private string namePostfix; [SerializeField] private List<Texture2D> texture2DList; private Vector2 selectScrollPosition = Vector2.zero; private void OnEnable() { texture2DList = new List<Texture2D>(); savePath = Application.dataPath; namePostfix = "copy"; trimSize = new Vector2Int(256, 256); serializedObject = new SerializedObject(this); texture2DListProperty = serializedObject.FindProperty("texture2DList"); } private void OnDisable() { serializedObject.Dispose(); } private void OnGUI() { selectScrollPosition = EditorGUILayout.BeginScrollView(selectScrollPosition, EditorStyles.helpBox); EditorGUILayout.PropertyField(texture2DListProperty, new GUIContent("选择图集"), true); EditorGUILayout.Space(); trimSize = EditorGUILayout.Vector2IntField("目标尺寸", trimSize); offset = EditorGUILayout.Vector2IntField("偏移", offset); EditorGUILayout.Space(); for (int i = 0; i < texture2DListProperty.arraySize; i++) { SerializedProperty texProperty = texture2DListProperty.GetArrayElementAtIndex(i); } EditorGUILayout.BeginHorizontal(); savePath = GUILayout.TextField(savePath); if (GUILayout.Button("选择保存路径")) { savePath = EditorUtility.OpenFolderPanel("路径选择", savePath, ""); } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("图片前缀"); namePrefix = EditorGUILayout.TextField(namePrefix); EditorGUILayout.LabelField("图片后缀"); namePostfix = EditorGUILayout.TextField(namePostfix); EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); EditorGUILayout.Space(); if (GUILayout.Button("保存", GUILayout.MinHeight(24), GUILayout.MinWidth(120))) { for (int i = 0; i < texture2DListProperty.arraySize; i++) { SerializedProperty texProperty = texture2DListProperty.GetArrayElementAtIndex(i); Texture2D item = texProperty.objectReferenceValue as Texture2D; Texture2D texture = new Texture2D(trimSize.x, trimSize.y, TextureFormat.RGBA32, item.streamingMipmaps); if (!item.isReadable) { Debug.LogWarning("请打开图片的 Read/Write Enable"); return; } int widthDifference = texture.width - item.width; int heightDifference = texture.height - item.height; widthDifference /= 2; heightDifference /= 2; widthDifference += offset.x; heightDifference += offset.y; Color[] itemPixels = item.GetPixels(); Color[] texturePixels = texture.GetPixels(); for (int j = 0; j < texturePixels.Length; j++) { int x = j % texture.width; int y = j / texture.width; x -= widthDifference; y -= heightDifference; int itemIndex = y * item.width + x; if (x >= 0 && y >= 0 && x < item.width && y < item.height && itemIndex < itemPixels.Length) { texturePixels[j] = itemPixels[itemIndex]; } else { texturePixels[j] = new Color(0, 0, 0, 0); } } texture.SetPixels(0, 0, texture.width, texture.height, texturePixels); texture.Apply(); string contents = Application.dataPath; byte[] bytes = texture.EncodeToPNG(); if (!Directory.Exists(contents)) Directory.CreateDirectory(contents); FileStream file = File.Open(string.Format("{0}/{1}{2}{3}.png", savePath, namePrefix, texProperty.objectReferenceValue.name, namePostfix), FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite); BinaryWriter writer = new BinaryWriter(file); writer.Write(bytes); file.Close(); EditorUtility.DisplayProgressBar("正在处理 : ", name, (float)i / texture2DListProperty.arraySize); } EditorUtility.ClearProgressBar(); AssetDatabase.Refresh(); } EditorGUILayout.EndScrollView(); } }
讲想要的图片拖到数组中,设置目标尺寸,保存路径,偏移(不设置默认居中),还有图片名称,点击保存就可以批量修改图片的尺寸了。
这个修改方式不会压缩图片,如果目标尺寸小于原图尺寸,可能图片会被切掉一部分。
关于名称的设置是采用前缀+图片原名+后缀的形式,这样产生的新图片会和原图片编号相同,如果原图带有数字以外的字母,可以自己修改代码正则匹配想要的数字。
这样可以快速的将所有图片修改为标准尺寸,并正确的压缩节省空间。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。