wuxianshengcong/Library/PackageCache/com.unity.2d.aseprite@1.1.6/Editor/Utilities/ImportUtilities.cs
2024-12-30 12:56:27 +08:00

324 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEditor.Animations;
using UnityEngine;
namespace UnityEditor.U2D.Aseprite
{
internal class UniqueNameGenerator
{
readonly Dictionary<int, HashSet<int>> m_NameHashes = new();
public string GetUniqueName(string name, int parentIndex = -1, bool logNewNameGenerated = false, UnityEngine.Object context = null)
{
if (!m_NameHashes.ContainsKey(parentIndex))
m_NameHashes.Add(parentIndex, new HashSet<int>());
var nameHashes = m_NameHashes[parentIndex];
return GetUniqueName(name, nameHashes, logNewNameGenerated, context);
}
static string GetUniqueName(string name, HashSet<int> stringHash, bool logNewNameGenerated = false, UnityEngine.Object context = null)
{
var sanitizedName = string.Copy(SanitizeName(name));
string uniqueName = sanitizedName;
int index = 1;
while (true)
{
var hash = GetStringHash(uniqueName);
if (!stringHash.Contains(hash))
{
stringHash.Add(hash);
if (logNewNameGenerated && sanitizedName != uniqueName)
Debug.Log($"Asset name {name} is changed to {uniqueName} to ensure uniqueness", context);
return uniqueName;
}
uniqueName = $"{sanitizedName}_{index}";
++index;
}
}
static string SanitizeName(string name)
{
name = name.Replace('\0', ' ');
string newName = null;
// We can't create asset name with these name.
if ((name.Length == 2 && name[0] == '.' && name[1] == '.')
|| (name.Length == 1 && name[0] == '.')
|| (name.Length == 1 && name[0] == '/'))
newName += name + "_";
if (!string.IsNullOrEmpty(newName))
{
Debug.LogWarning($"File contains layer with invalid name for generating asset. {name} is renamed to {newName}");
return newName;
}
return name;
}
static int GetStringHash(string str)
{
var md5Hasher = MD5.Create();
var hashed = md5Hasher.ComputeHash(Encoding.UTF8.GetBytes(str));
return BitConverter.ToInt32(hashed, 0);
}
}
internal static class ImportUtilities
{
public static void SaveAllPalettesToDisk(AsepriteFile file)
{
for (var i = 0; i < file.frameData.Count; ++i)
{
var frame = file.frameData[i];
for (var m = 0; m < frame.chunkCount; ++m)
{
var chunk = frame.chunks[m];
if (chunk.chunkType == ChunkTypes.Palette)
PaletteToDisk(chunk as PaletteChunk);
}
}
}
static void PaletteToDisk(PaletteChunk palette)
{
var noOfEntries = palette.noOfEntries;
const int cellSize = 32;
const int columns = 3;
var rows = Mathf.CeilToInt(noOfEntries / (float)3);
const int width = columns * cellSize;
var height = rows * cellSize;
var buffer = new Color32[width * height];
for (var i = 0; i < buffer.Length; ++i)
{
var x = i % width;
var y = i / width;
var imgColumn = x / 32;
var imgRow = y / 32;
var paletteEntry = imgColumn + (imgRow * columns);
if (paletteEntry < palette.noOfEntries)
buffer[i] = palette.entries[paletteEntry].color;
}
SaveToPng(buffer, width, height);
}
public static string SaveToPng(NativeArray<Color32> buffer, int width, int height)
{
return SaveToPng(buffer.ToArray(), width, height);
}
static string SaveToPng(Color32[] buffer, int width, int height)
{
if (width == 0 || height == 0)
return "No .png generated.";
var texture2D = new Texture2D(width, height);
texture2D.SetPixels32(buffer);
var png = texture2D.EncodeToPNG();
var path = Application.dataPath + $"/tex_{System.Guid.NewGuid().ToString()}.png";
var fileStream = System.IO.File.Create(path);
fileStream.Write(png);
fileStream.Close();
UnityEngine.Object.DestroyImmediate(texture2D);
return path;
}
public static void ExportAnimationAssets(AsepriteImporter[] importers, bool exportClips, bool exportController)
{
var savePath = EditorUtility.SaveFolderPanel(
"Export Animation Assets",
Application.dataPath, "");
ExportAnimationAssets(savePath, importers, exportClips, exportController);
}
public static void ExportAnimationAssets(string savePath, AsepriteImporter[] importers, bool exportClips, bool exportController)
{
if (string.IsNullOrEmpty(savePath))
return;
for (var i = 0; i < importers.Length; ++i)
{
var importedObjectPath = importers[i].assetPath;
AnimationClip[] clips;
if (exportClips)
clips = ExportAnimationClips(importedObjectPath, savePath);
else
clips = GetAllClipsFromController(importedObjectPath);
if (exportController)
ExportAnimatorController(importers[i], clips, savePath);
}
}
static AnimationClip[] ExportAnimationClips(string importedObjectPath, string path)
{
var relativePath = FileUtil.GetProjectRelativePath(path);
var animationClips = GetAllClipsFromController(importedObjectPath);
var clips = new List<AnimationClip>();
for (var i = 0; i < animationClips.Length; ++i)
{
var clip = animationClips[i];
var clipPath = $"{relativePath}/{clip.name}.anim";
var result = AssetDatabase.ExtractAsset(clip, clipPath);
if (!string.IsNullOrEmpty(result))
Debug.LogWarning(result);
var newClip = AssetDatabase.LoadAssetAtPath<AnimationClip>(clipPath);
clips.Add(newClip);
}
return clips.ToArray();
}
static AnimationClip[] GetAllClipsFromController(string assetPath)
{
var controller = AssetDatabase.LoadAssetAtPath<AnimatorController>(assetPath);
return controller.animationClips;
}
static void ExportAnimatorController(AsepriteImporter importer, AnimationClip[] clips, string path)
{
var relativePath = FileUtil.GetProjectRelativePath(path);
var importedObjectPath = importer.assetPath;
var fileName = System.IO.Path.GetFileNameWithoutExtension(importedObjectPath);
var controllerPath = $"{relativePath}/{fileName}.controller";
var controller = AnimatorController.CreateAnimatorControllerAtPath(controllerPath);
for (var i = 0; i < clips.Length; ++i)
controller.AddMotion(clips[i]);
}
public static Vector2 CalculateCellPivot(RectInt cellRect, uint spritePadding, Vector2Int canvasSize, SpriteAlignment alignment, Vector2 customPivot)
{
if (cellRect.width == 0 || cellRect.height == 0)
return Vector2.zero;
var scaleX = canvasSize.x / (float)cellRect.width;
var scaleY = canvasSize.y / (float)cellRect.height;
var halfSpritePadding = spritePadding / 2f;
var pivot = new Vector2((cellRect.x - halfSpritePadding) / (float)canvasSize.x, (cellRect.y - halfSpritePadding) / (float)canvasSize.y);
pivot *= -1f;
Vector2 alignmentPos;
if (alignment == SpriteAlignment.Custom)
alignmentPos = customPivot;
else
alignmentPos = PivotAlignmentToVector(alignment);
pivot.x += alignmentPos.x;
pivot.y += alignmentPos.y;
pivot.x *= scaleX;
pivot.y *= scaleY;
return pivot;
}
public static Vector2 PivotAlignmentToVector(SpriteAlignment alignment)
{
switch (alignment)
{
case SpriteAlignment.Center:
return new Vector2(0.5f, 0.5f);
case SpriteAlignment.TopLeft:
return new Vector2(0f, 1f);
case SpriteAlignment.TopCenter:
return new Vector2(0.5f, 1f);
case SpriteAlignment.TopRight:
return new Vector2(1f, 1f);
case SpriteAlignment.LeftCenter:
return new Vector2(0f, 0.5f);
case SpriteAlignment.RightCenter:
return new Vector2(1f, 0.5f);
case SpriteAlignment.BottomLeft:
return new Vector2(0f, 0f);
case SpriteAlignment.BottomCenter:
return new Vector2(0.5f, 0f);
case SpriteAlignment.BottomRight:
return new Vector2(1f, 0f);
default:
return new Vector2(0f, 0f);
}
}
public static string GetCellName(string baseName, int frameIndex, int noOfFrames, bool isMerged)
{
if (noOfFrames == 1)
return baseName;
return isMerged ? $"Frame_{frameIndex}" : $"{baseName}_Frame_{frameIndex}";
}
public static void DisposeIfCreated<T>(this NativeArray<T> arr) where T : struct
{
if (arr == default || !arr.IsCreated)
return;
var handle = NativeArrayUnsafeUtility.GetAtomicSafetyHandle(arr);
if (!AtomicSafetyHandle.IsHandleValid(handle))
return;
arr.Dispose();
}
public static bool IsLayerVisible(int layerIndex, IReadOnlyList<Layer> layers)
{
var layer = layers[layerIndex];
var isVisible = (layer.layerFlags & LayerFlags.Visible) != 0;
if (!isVisible)
return false;
if (layer.parentIndex != -1)
isVisible = IsLayerVisible(layer.parentIndex, layers);
return isVisible;
}
// Burst in 2021.x does not support passing native arrays to bursted methods.
#if UNITY_2022_2_OR_NEWER
[BurstCompile]
#endif
public static bool IsEmptyImage(in NativeArray<Color32> image)
{
for (var i = 0; i < image.Length; ++i)
{
if (image[i].a > 0)
return false;
}
return true;
}
#if !UNITY_2023_1_OR_NEWER
public static bool IsEqual(this RectInt rectA, RectInt rectB)
{
return rectA.x == rectB.x &&
rectA.y == rectB.y &&
rectA.width == rectB.width &&
rectA.height == rectB.height;
}
#endif
public static int FindIndex<T>(this IReadOnlyList<T> list, Func<T, bool> predicate)
{
for (var i = 0; i < list.Count; ++i)
{
if (predicate(list[i]))
return i;
}
return -1;
}
}
}