diff --git a/CHANGELOG.md b/CHANGELOG.md index ffd5885..cfeaee1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ # Changelog +## [6.0.6] - 2022-09-07 +### Fixed +- Improved import speed and memory allocation for psd/psb files by reducing the intermediate texture buffers. +- Fixed an editor freeze caused by over allocating intermediate texture buffers. (Case DANB-140) +- Fixed an issue where some layers would become invisible when merging multiple layers together. (Case DANB-131) +- Fixed exception when showing PSDImporter inspector. (DANB-198) ## [6.0.5] - 2022-07-21 ### Changed diff --git a/Editor/ImportUtilites.cs b/Editor/ImportUtilites.cs index 4ec29c2..927558d 100644 --- a/Editor/ImportUtilites.cs +++ b/Editor/ImportUtilites.cs @@ -1,8 +1,8 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Security.Cryptography; using System.Text; +using Unity.Collections; using UnityEngine; namespace UnityEditor.U2D.PSD @@ -87,4 +87,27 @@ public GameObject CreateGameObject(string name, params System.Type[] components) } } + internal static class ImportUtilities + { + public static string SaveToPng(NativeArray buffer, int width, int height) + { + if (!buffer.IsCreated || + buffer.Length == 0 || + width == 0 || + height == 0) + return "No .png generated."; + + var texture2D = new Texture2D(width, height); + texture2D.SetPixels32(buffer.ToArray()); + 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; + } + } } diff --git a/Editor/PSDImportData.cs b/Editor/PSDImportData.cs index e4f5204..ce2962b 100644 --- a/Editor/PSDImportData.cs +++ b/Editor/PSDImportData.cs @@ -5,6 +5,9 @@ namespace UnityEditor.U2D.PSD { + /// + /// Custom hidden asset to store meta information of the last import state + /// internal class PSDImportData : ScriptableObject { [SerializeField] @@ -16,7 +19,7 @@ public int importedTextureWidth } [SerializeField] - public int m_ImportedTextureHeight; + int m_ImportedTextureHeight; public int importedTextureHeight { get => m_ImportedTextureHeight; @@ -88,21 +91,42 @@ internal struct BoneGO public GameObject go; public int index; } - - internal class FlattenLayerData: IPSDLayerMappingStrategyComparable - { - public int layerID { get; set; } - public string name { get; set;} - public bool isGroup => true; - } [Serializable] - class PSDLayerImportSetting + class PSDLayerImportSetting: IPSDLayerMappingStrategyComparable { + [SerializeField] + string m_SpriteId; + GUID m_SpriteIDGUID; + public string name; public int layerId; public bool flatten; public bool isGroup; + + public int layerID => layerId; + string IPSDLayerMappingStrategyComparable.name => name; + bool IPSDLayerMappingStrategyComparable.isGroup => isGroup; + + public GUID spriteId + { + get + { + if (string.IsNullOrEmpty(m_SpriteId)) + { + m_SpriteIDGUID = GUID.Generate(); + m_SpriteId = m_SpriteIDGUID.ToString(); + } + + return m_SpriteIDGUID; + + } + set + { + m_SpriteIDGUID = value; + m_SpriteId = m_SpriteIDGUID.ToString(); + } + } } [Serializable] @@ -147,14 +171,24 @@ public bool isGroup get => m_IsGroup; set => m_IsGroup = value; } + + [SerializeField] + bool m_IsImported; + public bool isImported + { + get => m_IsImported; + set => m_IsImported = value; + } } + - [Serializable] - struct SpriteLayerMapping + /// + /// Data for extracting layers and colors from PSD + /// + class PSDExtractLayerData { - public string spriteId; - public int layerId; - public string layerName; + public BitmapLayer bitmapLayer; + public PSDLayerImportSetting importSetting; + public PSDExtractLayerData[] children; } -} - +} \ No newline at end of file diff --git a/Editor/PSDImporter.cs b/Editor/PSDImporter.cs index d470311..6dd0f3b 100644 --- a/Editor/PSDImporter.cs +++ b/Editor/PSDImporter.cs @@ -20,7 +20,7 @@ namespace UnityEditor.U2D.PSD /// ScriptedImporter to import Photoshop files /// // Version using unity release + 5 digit padding for future upgrade. Eg 2021.2 -> 21200000 - [ScriptedImporter(21200001, "psb", AllowCaching = true)] + [ScriptedImporter(21200002, "psb", AllowCaching = true)] [HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.psdimporter@latest")] [MovedFrom("UnityEditor.Experimental.AssetImporters")] public class PSDImporter : ScriptedImporter, ISpriteEditorDataProvider @@ -203,6 +203,8 @@ internal int textureActualHeight [SerializeField] SecondarySpriteTexture[] m_SecondarySpriteTextures; + + PSDExtractLayerData[] m_ExtractData; /// /// Implementation of ScriptedImporter.OnImportAsset @@ -213,10 +215,13 @@ internal int textureActualHeight /// public override void OnImportAsset(AssetImportContext ctx) { - FileStream fileStream = new FileStream(ctx.assetPath, FileMode.Open, FileAccess.Read); + var fileStream = new FileStream(ctx.assetPath, FileMode.Open, FileAccess.Read); Document doc = null; - m_ImportData = ScriptableObject.CreateInstance(); + + if(m_ImportData == null) + m_ImportData = ScriptableObject.CreateInstance(); m_ImportData.hideFlags = HideFlags.HideInHierarchy; + try { UnityEngine.Profiling.Profiler.BeginSample("OnImportAsset"); @@ -224,54 +229,60 @@ public override void OnImportAsset(AssetImportContext ctx) UnityEngine.Profiling.Profiler.BeginSample("PsdLoad"); doc = PaintDotNet.Data.PhotoshopFileType.PsdLoad.Load(fileStream); UnityEngine.Profiling.Profiler.EndSample(); + m_ImportData.CreatePSDLayerData(doc.Layers); - ValidatePSDLayerId(doc); + ValidatePSDLayerId(doc); + SetDocumentImportData(doc); + importData.documentSize = new Vector2Int(doc.width, doc.height); - bool singleSpriteMode = m_TextureImporterSettings.textureType == TextureImporterType.Sprite && m_TextureImporterSettings.spriteMode != (int)SpriteImportMode.Multiple; + var singleSpriteMode = m_TextureImporterSettings.textureType == TextureImporterType.Sprite && m_TextureImporterSettings.spriteMode != (int)SpriteImportMode.Multiple; EnsureSingleSpriteExist(); if (m_TextureImporterSettings.textureType != TextureImporterType.Sprite || m_MosaicLayers == false || singleSpriteMode) { - var image = new NativeArray(doc.width * doc.height, Allocator.Persistent); + var outputImageBuffer = new NativeArray(doc.width * doc.height, Allocator.Persistent); try { var spriteImportData = GetSpriteImportData(); - FlattenImageTask.Execute(doc.Layers, m_ImportHiddenLayers, doc.width, doc.height, image); - - int spriteCount = spriteDataCount; - int spriteIndexStart = 1; - + FlattenImageTask.Execute(m_ExtractData, ref outputImageBuffer, m_ImportHiddenLayers, documentSize); + if (spriteImportData.Count <= 0 || spriteImportData[0] == null) { spriteImportData.Add(new SpriteMetaData()); } + spriteImportData[0].name = System.IO.Path.GetFileNameWithoutExtension(ctx.assetPath) + "_1"; spriteImportData[0].alignment = (SpriteAlignment)m_TextureImporterSettings.spriteAlignment; spriteImportData[0].border = m_TextureImporterSettings.spriteBorder; spriteImportData[0].pivot = m_TextureImporterSettings.spritePivot; spriteImportData[0].rect = new Rect(0, 0, doc.width, doc.height); - if (singleSpriteMode) - { - spriteCount = 1; - spriteIndexStart = 0; - } importData.importedTextureWidth = textureActualWidth = doc.width; importData.importedTextureHeight = textureActualHeight = doc.height; - var output = ImportTexture(ctx, image, doc.width, doc.height, spriteIndexStart, spriteCount); + var spriteRects = new SpriteMetaData[0]; + if (m_TextureImporterSettings.textureType == TextureImporterType.Sprite) + { + if (singleSpriteMode) + spriteRects = new[] { spriteImportData[0] }; + else if (spriteImportData.Count > 1) + spriteRects = spriteImportData.GetRange(1, spriteDataCount).ToArray(); + } + + var output = ImportTexture(ctx, outputImageBuffer, doc.width, doc.height, spriteRects); importData.importedTextureWidth = output.texture.width; importData.importedTextureHeight = output.texture.height; + RegisterAssets(ctx, output); } finally { - image.Dispose(); + outputImageBuffer.Dispose(); } } else { - ImportFromLayers(ctx, doc); + ImportFromLayers(ctx); } if (!string.IsNullOrEmpty(m_SkeletonAssetReferenceID)) @@ -295,7 +306,7 @@ public override void OnImportAsset(AssetImportContext ctx) } - static void ValidatePSDLayerId(IEnumerable oldPsdLayer, List layers, UniqueNameGenerator uniqueNameGenerator) + static void ValidatePSDLayerId(IEnumerable oldPsdLayer, IEnumerable layers, UniqueNameGenerator uniqueNameGenerator) { // first check if all layers are unique. If not, we use back the previous layer id based on name match HashSet uniqueIdSet = new HashSet(); @@ -309,41 +320,41 @@ static void ValidatePSDLayerId(IEnumerable oldPsdLayer, List x.name == layers[i].Name); + var oldLayers = oldPsdLayer.Where(x => x.name == childBitmapLayer.Name); if (oldLayers.Count() == 0) - oldLayers = oldPsdLayer.Where(x => x.layerID == layers[i].Name.GetHashCode()); - + oldLayers = oldPsdLayer.Where(x => x.layerID == childBitmapLayer.Name.GetHashCode()); // pick one that is not already on the list foreach (var ol in oldLayers) { if (!uniqueNameGenerator.ContainHash(ol.layerID)) { - layers[i].LayerID = ol.layerID; + childBitmapLayer.LayerID = ol.layerID; break; } } } - if (uniqueNameGenerator.ContainHash(layers[i].LayerID)) + if (uniqueNameGenerator.ContainHash(childBitmapLayer.LayerID)) { - var importWarning = string.Format("Layer {0}: LayerId is not unique. Mapping will be done by Layer's name.", layers[i].Name); - var layerName = uniqueNameGenerator.GetUniqueName(layers[i].Name); - if (layerName != layers[i].Name) + var importWarning = string.Format("Layer {0}: LayerId is not unique. Mapping will be done by Layer's name.", childBitmapLayer.Name); + var layerName = uniqueNameGenerator.GetUniqueName(childBitmapLayer.Name); + if (layerName != childBitmapLayer.Name) importWarning += "\nLayer names are not unique. Please ensure they are unique to for SpriteRect to be mapped back correctly."; - layers[i].LayerID = layerName.GetHashCode(); + childBitmapLayer.LayerID = layerName.GetHashCode(); Debug.LogWarning(importWarning); } else - uniqueNameGenerator.AddHash(layers[i].LayerID); - if (layers[i].ChildLayer != null) + uniqueNameGenerator.AddHash(childBitmapLayer.LayerID); + if (childBitmapLayer.ChildLayer != null) { - ValidatePSDLayerId(oldPsdLayer, layers[i].ChildLayer, uniqueNameGenerator); + ValidatePSDLayerId(oldPsdLayer, childBitmapLayer.ChildLayer, uniqueNameGenerator); } } } @@ -358,7 +369,7 @@ void ValidatePSDLayerId(Document doc) } } - TextureGenerationOutput ImportTexture(AssetImportContext ctx, NativeArray imageData, int textureWidth, int textureHeight, int spriteStart, int spriteCount) + TextureGenerationOutput ImportTexture(AssetImportContext ctx, NativeArray imageData, int textureWidth, int textureHeight, SpriteMetaData[] sprites) { if (!imageData.IsCreated || imageData.Length == 0) return new TextureGenerationOutput(); @@ -394,15 +405,13 @@ TextureGenerationOutput ImportTexture(AssetImportContext ctx, NativeArray layers, PSDExtractLayerData[] extractData, IPSDLayerMappingStrategy mappingStrategy, List psdLayers, PSDExtractLayerData parent = null) { - NativeArray output = default(NativeArray); - - List layerIndex = new List(); - UniqueNameGenerator spriteNameHash = new UniqueNameGenerator(); - - var oldPsdLayers = GetPSDLayers(); - try + for (int i = 0; i < layers.Count(); ++i) { - var psdLayers = new List(); - var mappingStrategy = GetLayerMappingStrategy(); - - FlattenLayerData[] flattenLayerData = null; + var layer = layers.ElementAt(i); + PSDLayerImportSetting importSetting = null; if (m_PSDLayerImportSetting != null && m_PSDLayerImportSetting.Length > 0) { - flattenLayerData = m_PSDLayerImportSetting.Where(x => x.flatten).Select(y => new FlattenLayerData() + importSetting = m_PSDLayerImportSetting.FirstOrDefault(x => mappingStrategy.Compare(x, layer)); + } + var c = psdLayers?.FirstOrDefault(x => mappingStrategy.Compare(x, layer)); + if (c != null) + { + if(c.spriteID.Empty()) + c.spriteID = importSetting != null ? importSetting.spriteId : GUID.Generate(); + if (importSetting == null) { - layerID = y.layerId, - name = y.name - }).ToArray(); + importSetting = new PSDLayerImportSetting() + { + flatten = c.flatten, + }; + } + + importSetting.spriteId = c.spriteID; } - else + + if (importSetting == null) { - flattenLayerData = oldPsdLayers.Where(x => x.flatten).Select(y => new FlattenLayerData() + importSetting = new PSDLayerImportSetting() { - layerID = y.layerID, - name = y.name - }).ToArray(); + flatten = false + }; } - - ExtractLayerTask.Execute(psdLayers, doc.Layers, m_ImportHiddenLayers, flattenLayerData, mappingStrategy); + + extractData[i] = new PSDExtractLayerData() + { + bitmapLayer = layer, + importSetting = importSetting, + }; + + PSDExtractLayerData[] childrenextractData = null; + if (layer.ChildLayer != null) + { + childrenextractData = new PSDExtractLayerData[layer.ChildLayer.Count()]; + SetDocumentImportData(layer.ChildLayer, childrenextractData, mappingStrategy, psdLayers, extractData[i]); + } + + extractData[i].children = childrenextractData; + } + } + + void SetDocumentImportData(Document doc) + { + var oldPsdLayers = GetPSDLayers(); + var mappingStrategy = GetLayerMappingStrategy(); + m_ExtractData = new PSDExtractLayerData[doc.Layers.Count]; + SetDocumentImportData(doc.Layers, m_ExtractData, mappingStrategy, oldPsdLayers); + } + + void ImportFromLayers(AssetImportContext ctx) + { + var output = default(NativeArray); + + var layerIndex = new List(); + var spriteNameHash = new UniqueNameGenerator(); + + var oldPsdLayers = GetPSDLayers(); + List psdLayers = null; + try + { + ExtractLayerTask.Execute(in m_ExtractData, out psdLayers, m_ImportHiddenLayers, documentSize); + + var mappingStrategy = GetLayerMappingStrategy(); var layerUnique = mappingStrategy.LayersUnique(psdLayers.ConvertAll(x => (IPSDLayerMappingStrategyComparable)x)); if (!string.IsNullOrEmpty(layerUnique)) { @@ -495,29 +545,31 @@ void ImportFromLayers(AssetImportContext ctx, Document doc) hasNewLayer = true; } - int expectedBufferLength = doc.width * doc.height; var layerBuffers = new List>(); - for (int i = 0; i < psdLayers.Count; ++i) + var layerWidth = new List(); + var layerHeight = new List(); + for (var i = 0; i < psdLayers.Count; ++i) { var l = psdLayers[i]; + var expectedBufferLength = l.width * l.height; if (l.texture.IsCreated && l.texture.Length == expectedBufferLength && l.isImported) { layerBuffers.Add(l.texture); layerIndex.Add(i); + layerWidth.Add(l.width); + layerHeight.Add(l.height); } } - RectInt[] spritedata; - int width, height; - int padding = 4; - Vector2Int[] uvTransform; - ImagePacker.Pack(layerBuffers.ToArray(), doc.width, doc.height, padding, out output, out width, out height, out spritedata, out uvTransform); + const int padding = 4; + ImagePacker.Pack(layerBuffers.ToArray(), layerWidth.ToArray(), layerHeight.ToArray(), padding, out output, out int width, out int height, out RectInt[] spriteData, out Vector2Int[] uvTransform); + var spriteImportData = GetSpriteImportData(); if (spriteImportData.Count <= 0 || shouldResliceFromLayer || hasNewLayer) { var newSpriteMeta = new List(); - for (int i = 0; i < spritedata.Length && i < layerIndex.Count; ++i) + for (int i = 0; i < spriteData.Length && i < layerIndex.Count; ++i) { var psdLayer = psdLayers[layerIndex[i]]; var spriteSheet = spriteImportData.FirstOrDefault(x => x.spriteID == psdLayer.spriteID); @@ -527,26 +579,28 @@ void ImportFromLayers(AssetImportContext ctx, Document doc) spriteSheet.border = Vector4.zero; spriteSheet.alignment = (SpriteAlignment)m_TextureImporterSettings.spriteAlignment; spriteSheet.pivot = m_TextureImporterSettings.spritePivot; - spriteSheet.rect = new Rect(spritedata[i].x, spritedata[i].y, spritedata[i].width, spritedata[i].height); + spriteSheet.rect = new Rect(spriteData[i].x, spriteData[i].y, spriteData[i].width, spriteData[i].height); spriteSheet.spriteID = psdLayer.spriteID; } else { var r = spriteSheet.rect; - r.position = r.position - psdLayer.mosaicPosition + spritedata[i].position; + r.position = r.position - psdLayer.mosaicPosition + spriteData[i].position; spriteSheet.rect = r; } psdLayer.spriteName = GetUniqueSpriteName(psdLayer.name, spriteNameHash); spriteSheet.name = psdLayer.spriteName; + spriteSheet.spritePosition = psdLayer.layerPosition; + if(shouldResliceFromLayer) - spriteSheet.rect = new Rect(spritedata[i].x, spritedata[i].y, spritedata[i].width, spritedata[i].height); + spriteSheet.rect = new Rect(spriteData[i].x, spriteData[i].y, spriteData[i].width, spriteData[i].height); spriteSheet.uvTransform = uvTransform[i]; psdLayer.spriteID = spriteSheet.spriteID; - psdLayer.mosaicPosition = spritedata[i].position; + psdLayer.mosaicPosition = spriteData[i].position; newSpriteMeta.Add(spriteSheet); } spriteImportData.Clear(); @@ -557,29 +611,29 @@ void ImportFromLayers(AssetImportContext ctx, Document doc) spriteImportData.RemoveAll(x => removedLayersSprite.Contains(x.spriteID)); // First look for any user created SpriteRect and add those into the name hash - foreach (var spriteData in spriteImportData) + foreach (var importData in spriteImportData) { - var psdLayer = psdLayers.FirstOrDefault(x => x.spriteID == spriteData.spriteID); + var psdLayer = psdLayers.FirstOrDefault(x => x.spriteID == importData.spriteID); if (psdLayer == null) - spriteNameHash.AddHash(spriteData.name); + spriteNameHash.AddHash(importData.name); } - foreach (var spriteData in spriteImportData) + foreach (var importData in spriteImportData) { - var psdLayer = psdLayers.FirstOrDefault(x => x.spriteID == spriteData.spriteID); + var psdLayer = psdLayers.FirstOrDefault(x => x.spriteID == importData.spriteID); if (psdLayer == null) - spriteData.uvTransform = new Vector2Int((int)spriteData.rect.position.x, (int)spriteData.rect.position.y); + importData.uvTransform = new Vector2Int((int)importData.rect.position.x, (int)importData.rect.position.y); // If it is user created rect or the name has been changed before // add it into the spriteNameHash and we don't copy it over from the layer - if (psdLayer == null || psdLayer.spriteName != spriteData.name) - spriteNameHash.AddHash(spriteData.name); + if (psdLayer == null || psdLayer.spriteName != importData.name) + spriteNameHash.AddHash(importData.name); // If the sprite name has not been changed, we ensure the new // layer name is still unique and use it as the sprite name - if (psdLayer != null && psdLayer.spriteName == spriteData.name) + if (psdLayer != null && psdLayer.spriteName == importData.name) { psdLayer.spriteName = GetUniqueSpriteName(psdLayer.name, spriteNameHash); - spriteData.name = psdLayer.spriteName; + importData.name = psdLayer.spriteName; } } @@ -593,25 +647,28 @@ void ImportFromLayers(AssetImportContext ctx, Document doc) { spriteSheet = new SpriteMetaData(); spriteImportData.Add(spriteSheet); - spriteSheet.rect = new Rect(spritedata[k].x, spritedata[k].y, spritedata[k].width, spritedata[k].height); + spriteSheet.rect = new Rect(spriteData[k].x, spriteData[k].y, spriteData[k].width, spriteData[k].height); spriteSheet.border = Vector4.zero; spriteSheet.alignment = (SpriteAlignment)m_TextureImporterSettings.spriteAlignment; spriteSheet.pivot = m_TextureImporterSettings.spritePivot; + spriteSheet.spritePosition = psdLayers[i].layerPosition; psdLayers[i].spriteName = GetUniqueSpriteName(psdLayers[i].name, spriteNameHash); spriteSheet.name = psdLayers[i].spriteName; } else if (spriteSheet != null) { var r = spriteSheet.rect; - r.position = spriteSheet.rect.position - psdLayers[i].mosaicPosition + spritedata[k].position; + r.position = spriteSheet.rect.position - psdLayers[i].mosaicPosition + spriteData[k].position; + spriteSheet.rect = r; + spriteSheet.spritePosition = psdLayers[i].layerPosition; } if (spriteSheet != null) { spriteSheet.uvTransform = uvTransform[k]; psdLayers[i].spriteID = spriteSheet.spriteID; - psdLayers[i].mosaicPosition = spritedata[k].position; + psdLayers[i].mosaicPosition = spriteData[k].position; } } } @@ -623,7 +680,8 @@ void ImportFromLayers(AssetImportContext ctx, Document doc) oldPsdLayers.AddRange(psdLayers); importData.importedTextureHeight = textureActualHeight = height; importData.importedTextureWidth = textureActualWidth = width; - var generatedTexture = ImportTexture(ctx, output, width, height, 0, spriteImportData.Count); + + var generatedTexture = ImportTexture(ctx, output, width, height, spriteImportData.ToArray()); if (generatedTexture.texture) { @@ -1012,15 +1070,15 @@ GameObject OnProducePrefab(string assetname, Sprite[] sprites, SpriteLibraryAsse root.AddComponent().spriteLibraryAsset = spriteLib; var psdLayers = GetPSDLayers(); - for (int i = 0; i < psdLayers.Count; ++i) + for (var i = 0; i < psdLayers.Count; ++i) { BuildGroupGameObject(psdLayers, i, root.transform); } var boneGOs = CreateBonesGO(root.transform); - for (int i = 0; i < psdLayers.Count; ++i) + for (var i = 0; i < psdLayers.Count; ++i) { var l = psdLayers[i]; - GUID layerSpriteID = l.spriteID; + var layerSpriteID = l.spriteID; var sprite = sprites.FirstOrDefault(x => x.GetSpriteID() == layerSpriteID); var spriteMetaData = spriteImportData.FirstOrDefault(x => x.spriteID == layerSpriteID); if (sprite != null && spriteMetaData != null && l.gameObject != null) @@ -1028,10 +1086,17 @@ GameObject OnProducePrefab(string assetname, Sprite[] sprites, SpriteLibraryAsse var spriteRenderer = l.gameObject.AddComponent(); spriteRenderer.sprite = sprite; spriteRenderer.sortingOrder = psdLayers.Count - i; - var uvTransform = spriteMetaData.uvTransform; - var outlineOffset = new Vector2(spriteMetaData.rect.x - uvTransform.x + (spriteMetaData.pivot.x * spriteMetaData.rect.width), - spriteMetaData.rect.y - uvTransform.y + (spriteMetaData.pivot.y * spriteMetaData.rect.height)) * definitionScale / sprite.pixelsPerUnit; - l.gameObject.transform.position = new Vector3(outlineOffset.x, outlineOffset.y, 0); + + var pivot = spriteMetaData.pivot; + pivot.x *= spriteMetaData.rect.width; + pivot.y *= spriteMetaData.rect.height; + + var spritePosition = spriteMetaData.spritePosition; + spritePosition.x += pivot.x; + spritePosition.y += pivot.y; + spritePosition *= (definitionScale / sprite.pixelsPerUnit); + + l.gameObject.transform.position = new Vector3(spritePosition.x, spritePosition.y, 0f); if (characterSkeleton != null) { @@ -1491,10 +1556,7 @@ internal CharacterData characterData } } - internal Vector2Int documentSize - { - get => importData.documentSize; - } + internal Vector2Int documentSize => importData.documentSize; SpriteLibraryAsset ProduceSpriteLibAsset(Sprite[] sprites) { diff --git a/Editor/PSDImporterDataProvider.cs b/Editor/PSDImporterDataProvider.cs index 15b5f76..e383b3a 100644 --- a/Editor/PSDImporterDataProvider.cs +++ b/Editor/PSDImporterDataProvider.cs @@ -254,10 +254,12 @@ public CharacterData GetCharacterData() CharacterPart cp = srIndex == -1 ? new CharacterPart() : parts[srIndex]; cp.spriteId = spriteMetaData.spriteID.ToString(); cp.order = psdLayers.FindIndex(l => l.spriteID == spriteMetaData.spriteID); + cp.spritePosition = new RectInt(); - var uvTransform = spriteMetaData.uvTransform; - var outlineOffset = new Vector2(spriteMetaData.rect.x - uvTransform.x, spriteMetaData.rect.y - uvTransform.y); - cp.spritePosition.position = new Vector2Int((int)outlineOffset.x, (int)outlineOffset.y); + + var spritePos = spriteMetaData.spritePosition; + cp.spritePosition.position = new Vector2Int((int)spritePos.x, (int)spritePos.y); + cp.spritePosition.size = new Vector2Int((int)spriteMetaData.rect.width, (int)spriteMetaData.rect.height); cp.parentGroup = -1; //Find group @@ -266,8 +268,7 @@ public CharacterData GetCharacterData() { cp.parentGroup = ParentGroupInFlatten(spritePSDLayer.parentIndex, psdLayers); } - - + if (srIndex == -1) parts.Add(cp); else diff --git a/Editor/PSDImporterEditor.cs b/Editor/PSDImporterEditor.cs index 08d8b64..e92d71d 100644 --- a/Editor/PSDImporterEditor.cs +++ b/Editor/PSDImporterEditor.cs @@ -1,3 +1,4 @@ +#if USE_TEXTURE_PLATFORM_FIX using System; using System.Collections.Generic; using System.IO; @@ -67,6 +68,7 @@ struct InspectorGUI SerializedProperty m_SkeletonAssetReferenceID; SerializedProperty m_GeneratePhysicsShape; SerializedProperty m_LayerMappingOption; + SerializedProperty m_PlatformSettingsArrProp; private SkeletonAsset m_SkeletonAsset; readonly int[] m_FilterModeOptions = (int[])(Enum.GetValues(typeof(FilterMode))); @@ -134,6 +136,7 @@ public override void OnEnable() m_WrapU = textureImporterSettingsSP.FindPropertyRelative("m_WrapU"); m_WrapV = textureImporterSettingsSP.FindPropertyRelative("m_WrapV"); m_WrapW = textureImporterSettingsSP.FindPropertyRelative("m_WrapW"); + m_PlatformSettingsArrProp = extraDataSerializedObject.FindProperty("platformSettings"); foreach (var t in targets) { @@ -161,7 +164,7 @@ public override void OnEnable() MipMapGUI }; m_AdvanceInspectorGUI.Add(TextureImporterType.Default, advanceGUIAction); - LoadPlatformSettings(); + m_TexturePlatformSettingsHelper = new TexturePlatformSettingsHelper(this); m_ActiveEditorIndex = EditorPrefs.GetInt(this.GetType().Name + "ActiveEditorIndex", 0); @@ -182,6 +185,28 @@ public override void OnEnable() UpdateLayerTreeView(); } + /// + /// Override for AssetImporter.extraDataType + /// + protected override Type extraDataType => typeof(PSDImporterEditorExternalData); + + /// + /// Override for AssetImporter.InitializeExtraDataInstance + /// + /// Target object + /// Target index + protected override void InitializeExtraDataInstance(UnityEngine.Object extraTarget, int targetIndex) + { + var importer = targets[targetIndex] as PSDImporter; + var extraData = extraTarget as PSDImporterEditorExternalData; + var platformSettingsNeeded = TexturePlatformSettingsHelper.PlatformSettingsNeeded(this); + if (importer != null) + { + extraData.Init(importer, platformSettingsNeeded); + } + + } + void UpdateLayerTreeView() { if (!ReferenceEquals(m_CurrentTarget,target) || m_LayerTreeView == null) @@ -207,6 +232,7 @@ public override void OnInspectorGUI() { Profiler.BeginSample("PSDImporter.OnInspectorGUI"); serializedObject.Update(); + extraDataSerializedObject.Update(); if (s_Styles == null) s_Styles = new Styles(); @@ -232,6 +258,7 @@ public override void OnInspectorGUI() m_InspectorUI[m_ActiveEditorIndex].callback(); serializedObject.ApplyModifiedProperties(); + extraDataSerializedObject.ApplyModifiedProperties(); ApplyRevertGUI(); Profiler.EndSample(); } @@ -273,7 +300,6 @@ void DoSettingsUI() CommonTextureSettingsGUI(); DoPlatformSettings(); DoAdvanceInspector(); - } void MainRigPropertyField() @@ -316,10 +342,25 @@ protected override void Apply() doc.Cleanup(); AnalyticFactory.analytics.SendApplyEvent(evt); m_TexturePlatformSettingsHelper.Apply(); + ApplyTexturePlatformSettings(); base.Apply(); PSDImportPostProcessor.currentApplyAssetPath = ((PSDImporter) target).assetPath; } + void ApplyTexturePlatformSettings() + { + for(int i = 0; i< targets.Length && i < extraDataTargets.Length; ++i) + { + var psdImporter = (PSDImporter)targets[i]; + var externalData = (PSDImporterEditorExternalData)extraDataTargets[i]; + foreach (var ps in externalData.platformSettings) + { + psdImporter.SetPlatformTextureSettings(ps); + psdImporter.Apply(); + } + } + } + static bool IsPSD(PsdFile doc) { return !doc.IsLargeDocument; @@ -354,63 +395,6 @@ bool IsCharacterRigged() return false; } - Dictionary> m_PlatfromSettings = new Dictionary>(); - void LoadPlatformSettings() - { - foreach (var t in targets) - { - var importer = ((PSDImporter)t); - var importerPlatformSettings = importer.GetAllPlatformSettings(); - for (int i = 0; i < importerPlatformSettings.Length; ++i) - { - var tip = importerPlatformSettings[i]; - List platformSettings = null; - m_PlatfromSettings.TryGetValue(tip.name, out platformSettings); - if (platformSettings == null) - { - platformSettings = new List(); - m_PlatfromSettings.Add(tip.name, platformSettings); - } - platformSettings.Add(tip); - } - } - } - - void StorePlatformSettings() - { - var platformSettingsSP = serializedObject.FindProperty("m_PlatformSettings"); - platformSettingsSP.ClearArray(); - foreach (var keyValue in m_PlatfromSettings) - { - if (!keyValue.Value[0].overridden) - continue; - - SerializedProperty platformSettingSP = null; - for (int i = 0; i < platformSettingsSP.arraySize; ++i) - { - var sp = platformSettingsSP.GetArrayElementAtIndex(i); - if (sp.FindPropertyRelative("m_Name").stringValue == keyValue.Key) - platformSettingSP = sp; - } - if (platformSettingSP == null) - { - platformSettingsSP.InsertArrayElementAtIndex(platformSettingsSP.arraySize); - platformSettingSP = platformSettingsSP.GetArrayElementAtIndex(platformSettingsSP.arraySize - 1); - } - - var tip = keyValue.Value[0]; - platformSettingSP.FindPropertyRelative("m_Name").stringValue = tip.name; - platformSettingSP.FindPropertyRelative("m_Overridden").intValue = tip.overridden ? 1 : 0; - platformSettingSP.FindPropertyRelative("m_MaxTextureSize").intValue = tip.maxTextureSize; - platformSettingSP.FindPropertyRelative("m_ResizeAlgorithm").intValue = (int)tip.resizeAlgorithm; - platformSettingSP.FindPropertyRelative("m_TextureFormat").intValue = (int)tip.format; - platformSettingSP.FindPropertyRelative("m_TextureCompression").intValue = (int)tip.textureCompression; - platformSettingSP.FindPropertyRelative("m_CompressionQuality").intValue = tip.compressionQuality; - platformSettingSP.FindPropertyRelative("m_CrunchedCompression").intValue = tip.crunchedCompression ? 1 : 0; - platformSettingSP.FindPropertyRelative("m_AllowsAlphaSplitting").intValue = tip.allowsAlphaSplitting ? 1 : 0; - } - } - void DoPlatformSettings() { if (m_EditorFoldOutState.DoPlatformSettingsUI(s_Styles.platformSettingsHeaderText)) @@ -920,7 +904,6 @@ void ExportMosaicTexture() protected override void ResetValues() { base.ResetValues(); - LoadPlatformSettings(); m_TexturePlatformSettingsHelper = new TexturePlatformSettingsHelper(this); } @@ -941,9 +924,15 @@ public int GetTargetCount() /// TextureImporterPlatformSettings for the given platform name and selected target index. public TextureImporterPlatformSettings GetPlatformTextureSettings(int i, string name) { - if(m_PlatfromSettings.ContainsKey(name)) - if(m_PlatfromSettings[name].Count > i) - return m_PlatfromSettings[name][i]; + var externalData = extraDataSerializedObject.targetObjects[i] as PSDImporterEditorExternalData; + if (externalData != null) + { + foreach (var ps in externalData.platformSettings) + { + if (ps.name == name) + return ps; + } + } return new TextureImporterPlatformSettings() { name = name, @@ -987,9 +976,9 @@ public bool IsSourceTextureHDR(int i) /// TextureImporterPlatformSettings to apply to target. public void SetPlatformTextureSettings(int i, TextureImporterPlatformSettings platformSettings) { - var psdImporter = ((PSDImporter)targets[i]); - psdImporter.SetPlatformTextureSettings(platformSettings); - psdImporter.Apply(); + // var psdImporter = ((PSDImporter)targets[i]); + // psdImporter.SetPlatformTextureSettings(platformSettings); + // psdImporter.Apply(); } /// @@ -1004,6 +993,21 @@ public void GetImporterSettings(int i, TextureImporterSettings settings) GetSerializedPropertySettings(settings); } + /// + /// Retrieves the build target name property of a TextureImporterPlatformSettings + /// + /// The SerializedProperty of a TextureImporterPlatformSettings + /// + string ITexturePlatformSettingsDataProvider.GetBuildTargetName(SerializedProperty sp) + { + return sp.FindPropertyRelative("m_Name").stringValue; + } + + /// + /// Returns SerializedProperty for TextureImporterPlatformSettings array. + /// + SerializedProperty ITexturePlatformSettingsDataProvider.platformSettingsArray => m_PlatformSettingsArrProp; + internal TextureImporterSettings GetSerializedPropertySettings(TextureImporterSettings settings) { if (!m_AlphaSource.hasMultipleDifferentValues) @@ -1449,3 +1453,4 @@ public static implicit operator bool(SavedBool s) } } } +#endif \ No newline at end of file diff --git a/Editor/PSDImporterEditorExternalData.cs b/Editor/PSDImporterEditorExternalData.cs new file mode 100644 index 0000000..5bab688 --- /dev/null +++ b/Editor/PSDImporterEditorExternalData.cs @@ -0,0 +1,39 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEditor.U2D.Common; +using UnityEngine; + +namespace UnityEditor.U2D.PSD +{ + internal class PSDImporterEditorExternalData : ScriptableObject + { + [SerializeField] + public List platformSettings = new List(); + + public void Init(PSDImporter importer, IList platformSettingsNeeded) + { + var importerPlatformSettings = importer.GetAllPlatformSettings(); + + for (int i = 0; i < importerPlatformSettings.Length; ++i) + { + var tip = importerPlatformSettings[i]; + var setting = platformSettings.FirstOrDefault(x => x.name == tip.name); + if (setting == null) + { + platformSettings.Add(tip); + } + } + + foreach (var ps in platformSettingsNeeded) + { + var setting = platformSettings.FirstOrDefault(x => x.name == ps.name); + if (setting == null) + { + platformSettings.Add(ps); + } + } + } + } +} + diff --git a/Editor/PSDImporterEditorExternalData.cs.meta b/Editor/PSDImporterEditorExternalData.cs.meta new file mode 100644 index 0000000..45064c3 --- /dev/null +++ b/Editor/PSDImporterEditorExternalData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f90bf72f9f73a4838839f7d5e2111b1b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/PSDImproterEditorBefore_2021_3_9f1.cs b/Editor/PSDImproterEditorBefore_2021_3_9f1.cs new file mode 100644 index 0000000..2bd1912 --- /dev/null +++ b/Editor/PSDImproterEditorBefore_2021_3_9f1.cs @@ -0,0 +1,1453 @@ +#if !USE_TEXTURE_PLATFORM_FIX +using System; +using System.Collections.Generic; +using System.IO; +using PhotoshopFile; +using UnityEditor.AssetImporters; +using UnityEditor.IMGUI.Controls; +using UnityEditor.U2D.Animation; +using UnityEditor.U2D.Common; +using UnityEditor.U2D.Sprites; +using UnityEngine; +using UnityEngine.Profiling; +using UnityEngine.Scripting.APIUpdating; +using UnityEngine.U2D.Animation; +using UnityEngine.U2D.Common; + +namespace UnityEditor.U2D.PSD +{ + /// + /// Inspector for PSDImporter + /// + [CustomEditor(typeof(PSDImporter))] + [MovedFrom("UnityEditor.Experimental.AssetImporters")] + public class PSDImporterEditor : ScriptedImporterEditor, ITexturePlatformSettingsDataProvider + { + struct InspectorGUI + { + public Action callback; + public bool needsRepaint; + } + SerializedProperty m_TextureType; + SerializedProperty m_TextureShape; + SerializedProperty m_SpriteMode; + SerializedProperty m_SpritePixelsToUnits; + SerializedProperty m_SpriteMeshType; + SerializedProperty m_SpriteExtrude; + SerializedProperty m_Alignment; + SerializedProperty m_SpritePivot; + SerializedProperty m_NPOTScale; + SerializedProperty m_IsReadable; + SerializedProperty m_sRGBTexture; + SerializedProperty m_AlphaSource; + SerializedProperty m_MipMapMode; + SerializedProperty m_EnableMipMap; + SerializedProperty m_FadeOut; + SerializedProperty m_BorderMipMap; + SerializedProperty m_MipMapsPreserveCoverage; + SerializedProperty m_AlphaTestReferenceValue; + SerializedProperty m_MipMapFadeDistanceStart; + SerializedProperty m_MipMapFadeDistanceEnd; + SerializedProperty m_AlphaIsTransparency; + SerializedProperty m_FilterMode; + SerializedProperty m_Aniso; + + SerializedProperty m_WrapU; + SerializedProperty m_WrapV; + SerializedProperty m_WrapW; + SerializedProperty m_ConvertToNormalMap; + SerializedProperty m_MosaicLayers; + SerializedProperty m_ImportHiddenLayers; + SerializedProperty m_ResliceFromLayer; + SerializedProperty m_CharacterMode; + SerializedProperty m_DocumentPivot; + SerializedProperty m_DocumentAlignment; + SerializedProperty m_GenerateGOHierarchy; + SerializedProperty m_PaperDollMode; + SerializedProperty m_KeepDupilcateSpriteName; + SerializedProperty m_SkeletonAssetReferenceID; + SerializedProperty m_GeneratePhysicsShape; + SerializedProperty m_LayerMappingOption; + + private SkeletonAsset m_SkeletonAsset; + readonly int[] m_FilterModeOptions = (int[])(Enum.GetValues(typeof(FilterMode))); + + bool m_IsPOT = false; + Dictionary m_AdvanceInspectorGUI = new Dictionary(); + int m_PlatformSettingsIndex; + bool m_ShowPerAxisWrapModes = false; + int m_ActiveEditorIndex = 0; + + TexturePlatformSettingsHelper m_TexturePlatformSettingsHelper; + + TexturePlatformSettingsView m_TexturePlatformSettingsView = new TexturePlatformSettingsView(); + TexturePlatformSettingsController m_TexturePlatformSettingsController = new TexturePlatformSettingsController(); + + PSDImporterEditorFoldOutState m_EditorFoldOutState = new PSDImporterEditorFoldOutState(); + InspectorGUI[] m_InspectorUI; + PSDImporterEditorLayerTreeView m_LayerTreeView; + TreeViewState m_TreeViewState; + PSDImporter m_CurrentTarget; + /// + /// Implementation of AssetImporterEditor.OnEnable + /// + public override void OnEnable() + { + base.OnEnable(); + m_MosaicLayers = serializedObject.FindProperty("m_MosaicLayers"); + m_ImportHiddenLayers = serializedObject.FindProperty("m_ImportHiddenLayers"); + m_ResliceFromLayer = serializedObject.FindProperty("m_ResliceFromLayer"); + m_CharacterMode = serializedObject.FindProperty("m_CharacterMode"); + m_DocumentPivot = serializedObject.FindProperty("m_DocumentPivot"); + m_DocumentAlignment = serializedObject.FindProperty("m_DocumentAlignment"); + m_GenerateGOHierarchy = serializedObject.FindProperty("m_GenerateGOHierarchy"); + m_PaperDollMode = serializedObject.FindProperty("m_PaperDollMode"); + m_KeepDupilcateSpriteName = serializedObject.FindProperty("m_KeepDupilcateSpriteName"); + m_SkeletonAssetReferenceID = serializedObject.FindProperty("m_SkeletonAssetReferenceID"); + m_GeneratePhysicsShape = serializedObject.FindProperty("m_GeneratePhysicsShape"); + m_LayerMappingOption = serializedObject.FindProperty("m_LayerMappingOption"); + + var textureImporterSettingsSP = serializedObject.FindProperty("m_TextureImporterSettings"); + m_TextureType = textureImporterSettingsSP.FindPropertyRelative("m_TextureType"); + m_TextureShape = textureImporterSettingsSP.FindPropertyRelative("m_TextureShape"); + m_ConvertToNormalMap = textureImporterSettingsSP.FindPropertyRelative("m_ConvertToNormalMap"); + m_SpriteMode = textureImporterSettingsSP.FindPropertyRelative("m_SpriteMode"); + m_SpritePixelsToUnits = textureImporterSettingsSP.FindPropertyRelative("m_SpritePixelsToUnits"); + m_SpriteMeshType = textureImporterSettingsSP.FindPropertyRelative("m_SpriteMeshType"); + m_SpriteExtrude = textureImporterSettingsSP.FindPropertyRelative("m_SpriteExtrude"); + m_Alignment = textureImporterSettingsSP.FindPropertyRelative("m_Alignment"); + m_SpritePivot = textureImporterSettingsSP.FindPropertyRelative("m_SpritePivot"); + m_NPOTScale = textureImporterSettingsSP.FindPropertyRelative("m_NPOTScale"); + m_IsReadable = textureImporterSettingsSP.FindPropertyRelative("m_IsReadable"); + m_sRGBTexture = textureImporterSettingsSP.FindPropertyRelative("m_sRGBTexture"); + m_AlphaSource = textureImporterSettingsSP.FindPropertyRelative("m_AlphaSource"); + m_MipMapMode = textureImporterSettingsSP.FindPropertyRelative("m_MipMapMode"); + m_EnableMipMap = textureImporterSettingsSP.FindPropertyRelative("m_EnableMipMap"); + m_FadeOut = textureImporterSettingsSP.FindPropertyRelative("m_FadeOut"); + m_BorderMipMap = textureImporterSettingsSP.FindPropertyRelative("m_BorderMipMap"); + m_MipMapsPreserveCoverage = textureImporterSettingsSP.FindPropertyRelative("m_MipMapsPreserveCoverage"); + m_AlphaTestReferenceValue = textureImporterSettingsSP.FindPropertyRelative("m_AlphaTestReferenceValue"); + m_MipMapFadeDistanceStart = textureImporterSettingsSP.FindPropertyRelative("m_MipMapFadeDistanceStart"); + m_MipMapFadeDistanceEnd = textureImporterSettingsSP.FindPropertyRelative("m_MipMapFadeDistanceEnd"); + m_AlphaIsTransparency = textureImporterSettingsSP.FindPropertyRelative("m_AlphaIsTransparency"); + m_FilterMode = textureImporterSettingsSP.FindPropertyRelative("m_FilterMode"); + m_Aniso = textureImporterSettingsSP.FindPropertyRelative("m_Aniso"); + m_WrapU = textureImporterSettingsSP.FindPropertyRelative("m_WrapU"); + m_WrapV = textureImporterSettingsSP.FindPropertyRelative("m_WrapV"); + m_WrapW = textureImporterSettingsSP.FindPropertyRelative("m_WrapW"); + + foreach (var t in targets) + { + m_IsPOT &= ((PSDImporter)t).isNPOT; + } + + + var assetPath = AssetDatabase.GUIDToAssetPath(m_SkeletonAssetReferenceID.stringValue); + m_SkeletonAsset = AssetDatabase.LoadAssetAtPath(assetPath); + + var advanceGUIAction = new Action[] + { + ColorSpaceGUI, + AlphaHandlingGUI, + POTScaleGUI, + ReadableGUI, + MipMapGUI + }; + m_AdvanceInspectorGUI.Add(TextureImporterType.Sprite, advanceGUIAction); + + advanceGUIAction = new Action[] + { + POTScaleGUI, + ReadableGUI, + MipMapGUI + }; + m_AdvanceInspectorGUI.Add(TextureImporterType.Default, advanceGUIAction); + LoadPlatformSettings(); + m_TexturePlatformSettingsHelper = new TexturePlatformSettingsHelper(this); + + m_ActiveEditorIndex = EditorPrefs.GetInt(this.GetType().Name + "ActiveEditorIndex", 0); + m_InspectorUI = new [] + { + new InspectorGUI() + { + callback = DoSettingsUI, + needsRepaint = false + }, + new InspectorGUI() + { + callback = DoLayerManagementUI, + needsRepaint = true + } + }; + m_TreeViewState = new TreeViewState(); + UpdateLayerTreeView(); + } + + void UpdateLayerTreeView() + { + if (!ReferenceEquals(m_CurrentTarget,target) || m_LayerTreeView == null) + { + m_CurrentTarget = (PSDImporter)target; + m_LayerTreeView = new PSDImporterEditorLayerTreeView(assetTarget.name, m_TreeViewState, m_CurrentTarget.importData.psdLayerData, m_ImportHiddenLayers.boolValue, serializedObject.FindProperty("m_PSDLayerImportSetting"), m_CurrentTarget.GetLayerMappingStrategy()); + } + } + + /// + /// Override from AssetImporterEditor.RequiresConstantRepaint + /// + /// Returns true when in Layer Management tab for UI feedback update, false otherwise. + public override bool RequiresConstantRepaint() + { + return m_InspectorUI[m_ActiveEditorIndex].needsRepaint; + } + + /// + /// Implementation of AssetImporterEditor.OnInspectorGUI + /// + public override void OnInspectorGUI() + { + Profiler.BeginSample("PSDImporter.OnInspectorGUI"); + serializedObject.Update(); + if (s_Styles == null) + s_Styles = new Styles(); + + UpdateLayerTreeView(); + + GUILayout.Space(5); + using (new GUILayout.HorizontalScope()) + { + GUILayout.FlexibleSpace(); + using (var check = new EditorGUI.ChangeCheckScope()) + { + m_ActiveEditorIndex = GUILayout.Toolbar(m_ActiveEditorIndex, s_Styles.editorTabNames, "LargeButton", GUI.ToolbarButtonSize.FitToContents); + if (check.changed) + { + EditorPrefs.SetInt(GetType().Name + "ActiveEditorIndex", m_ActiveEditorIndex); + } + } + GUILayout.FlexibleSpace(); + } + GUILayout.Space(5); + + + m_InspectorUI[m_ActiveEditorIndex].callback(); + + serializedObject.ApplyModifiedProperties(); + ApplyRevertGUI(); + Profiler.EndSample(); + } + + void DoLayerManagementUI() + { + var rect = InternalEngineBridge.GetGUIClipVisibleRect(); + //Magic number. 54 is for header. The rest no idea. + rect = GUILayoutUtility.GetRect(0,rect.height - (54 + 120), GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true)); + m_LayerTreeView.OnGUI(rect); + } + + void DoSettingsUI() + { + if (m_EditorFoldOutState.DoGeneralUI(s_Styles.generalHeaderText)) + { + EditorGUI.showMixedValue = m_TextureType.hasMultipleDifferentValues; + m_TextureType.intValue = EditorGUILayout.IntPopup(s_Styles.textureTypeTitle, m_TextureType.intValue, s_Styles.textureTypeOptions, s_Styles.textureTypeValues); + EditorGUI.showMixedValue = false; + + switch ((TextureImporterType)m_TextureType.intValue) + { + case TextureImporterType.Sprite: + DoSpriteTextureTypeInspector(); + break; + case TextureImporterType.Default: + DoTextureDefaultTextureTypeInspector(); + break; + default: + Debug.LogWarning("We only support Default or Sprite texture type for now. Texture type is set to default."); + m_TextureType.intValue = (int)TextureImporterType.Default; + break; + } + GUILayout.Space(5); + } + + if ((TextureImporterType)m_TextureType.intValue == TextureImporterType.Sprite) + DoSpriteInspector(); + CommonTextureSettingsGUI(); + DoPlatformSettings(); + DoAdvanceInspector(); + + } + + void MainRigPropertyField() + { + EditorGUI.BeginChangeCheck(); + m_SkeletonAsset = EditorGUILayout.ObjectField(s_Styles.mainSkeletonName, m_SkeletonAsset, typeof(SkeletonAsset), false) as SkeletonAsset; + if (EditorGUI.EndChangeCheck()) + { + var referencePath = AssetDatabase.GetAssetPath(m_SkeletonAsset); + if (referencePath == ((AssetImporter) target).assetPath) + m_SkeletonAssetReferenceID.stringValue = ""; + else + m_SkeletonAssetReferenceID.stringValue = AssetDatabase.GUIDFromAssetPath(referencePath).ToString(); + } + } + + /// + /// Implementation of AssetImporterEditor.Apply + /// + protected override void Apply() + { + InternalEditorBridge.ApplySpriteEditorWindow(); + FileStream fileStream = new FileStream(((AssetImporter)target).assetPath, FileMode.Open, FileAccess.Read); + var doc = PaintDotNet.Data.PhotoshopFileType.PsdLoad.Load(fileStream, ELoadFlag.Header | ELoadFlag.ColorMode); + + PSDApplyEvent evt = new PSDApplyEvent() + { + instance_id = target.GetInstanceID(), + texture_type = m_TextureType.intValue, + sprite_mode = m_SpriteMode.intValue, + mosaic_layer = m_MosaicLayers.boolValue, + import_hidden_layer = m_ImportHiddenLayers.boolValue, + character_mode = m_CharacterMode.boolValue, + generate_go_hierarchy = m_GenerateGOHierarchy.boolValue, + reslice_from_layer = m_ResliceFromLayer.boolValue, + is_character_rigged = IsCharacterRigged(), + is_psd = IsPSD(doc), + color_mode = FileColorMode(doc) + }; + doc.Cleanup(); + AnalyticFactory.analytics.SendApplyEvent(evt); + m_TexturePlatformSettingsHelper.Apply(); + base.Apply(); + PSDImportPostProcessor.currentApplyAssetPath = ((PSDImporter) target).assetPath; + } + + static bool IsPSD(PsdFile doc) + { + return !doc.IsLargeDocument; + } + + static PsdColorMode FileColorMode(PsdFile doc) + { + return doc.ColorMode; + } + + bool IsCharacterRigged() + { + var importer = target as PSDImporter; + if (importer != null) + { + var characterProvider = importer.GetDataProvider(); + var meshDataProvider = importer.GetDataProvider(); + if (characterProvider != null && meshDataProvider != null) + { + var character = characterProvider.GetCharacterData(); + foreach (var parts in character.parts) + { + var vert = meshDataProvider.GetVertices(new GUID(parts.spriteId)); + var indices = meshDataProvider.GetIndices(new GUID(parts.spriteId)); + if (parts.bones != null && parts.bones.Length > 0 && + vert != null && vert.Length > 0 && + indices != null && indices.Length > 0) + return true; + } + } + } + return false; + } + + Dictionary> m_PlatfromSettings = new Dictionary>(); + void LoadPlatformSettings() + { + foreach (var t in targets) + { + var importer = ((PSDImporter)t); + var importerPlatformSettings = importer.GetAllPlatformSettings(); + for (int i = 0; i < importerPlatformSettings.Length; ++i) + { + var tip = importerPlatformSettings[i]; + List platformSettings = null; + m_PlatfromSettings.TryGetValue(tip.name, out platformSettings); + if (platformSettings == null) + { + platformSettings = new List(); + m_PlatfromSettings.Add(tip.name, platformSettings); + } + platformSettings.Add(tip); + } + } + } + + void StorePlatformSettings() + { + var platformSettingsSP = serializedObject.FindProperty("m_PlatformSettings"); + platformSettingsSP.ClearArray(); + foreach (var keyValue in m_PlatfromSettings) + { + if (!keyValue.Value[0].overridden) + continue; + + SerializedProperty platformSettingSP = null; + for (int i = 0; i < platformSettingsSP.arraySize; ++i) + { + var sp = platformSettingsSP.GetArrayElementAtIndex(i); + if (sp.FindPropertyRelative("m_Name").stringValue == keyValue.Key) + platformSettingSP = sp; + } + if (platformSettingSP == null) + { + platformSettingsSP.InsertArrayElementAtIndex(platformSettingsSP.arraySize); + platformSettingSP = platformSettingsSP.GetArrayElementAtIndex(platformSettingsSP.arraySize - 1); + } + + var tip = keyValue.Value[0]; + platformSettingSP.FindPropertyRelative("m_Name").stringValue = tip.name; + platformSettingSP.FindPropertyRelative("m_Overridden").intValue = tip.overridden ? 1 : 0; + platformSettingSP.FindPropertyRelative("m_MaxTextureSize").intValue = tip.maxTextureSize; + platformSettingSP.FindPropertyRelative("m_ResizeAlgorithm").intValue = (int)tip.resizeAlgorithm; + platformSettingSP.FindPropertyRelative("m_TextureFormat").intValue = (int)tip.format; + platformSettingSP.FindPropertyRelative("m_TextureCompression").intValue = (int)tip.textureCompression; + platformSettingSP.FindPropertyRelative("m_CompressionQuality").intValue = tip.compressionQuality; + platformSettingSP.FindPropertyRelative("m_CrunchedCompression").intValue = tip.crunchedCompression ? 1 : 0; + platformSettingSP.FindPropertyRelative("m_AllowsAlphaSplitting").intValue = tip.allowsAlphaSplitting ? 1 : 0; + } + } + + void DoPlatformSettings() + { + if (m_EditorFoldOutState.DoPlatformSettingsUI(s_Styles.platformSettingsHeaderText)) + { + GUILayout.Space(5); + m_TexturePlatformSettingsHelper.ShowPlatformSpecificSettings(); + GUILayout.Space(5); + } + } + + void DoAdvanceInspector() + { + if (!m_TextureType.hasMultipleDifferentValues) + { + if (m_AdvanceInspectorGUI.ContainsKey((TextureImporterType)m_TextureType.intValue)) + { + if (m_EditorFoldOutState.DoAdvancedUI(s_Styles.advancedHeaderText)) + { + foreach (var action in m_AdvanceInspectorGUI[(TextureImporterType)m_TextureType.intValue]) + { + action(); + } + } + } + } + } + + void CommonTextureSettingsGUI() + { + if (m_EditorFoldOutState.DoTextureUI(s_Styles.textureHeaderText)) + { + EditorGUI.BeginChangeCheck(); + + // Wrap mode + bool isVolume = false; + WrapModePopup(m_WrapU, m_WrapV, m_WrapW, isVolume, ref m_ShowPerAxisWrapModes); + + + // Display warning about repeat wrap mode on restricted npot emulation + if (m_NPOTScale.intValue == (int)TextureImporterNPOTScale.None && + (m_WrapU.intValue == (int)TextureWrapMode.Repeat || m_WrapV.intValue == (int)TextureWrapMode.Repeat) && + !InternalEditorBridge.DoesHardwareSupportsFullNPOT()) + { + bool displayWarning = false; + foreach (var target in targets) + { + var imp = (PSDImporter)target; + int w = imp.textureActualWidth; + int h = imp.textureActualHeight; + if (!Mathf.IsPowerOfTwo(w) || !Mathf.IsPowerOfTwo(h)) + { + displayWarning = true; + break; + } + } + + if (displayWarning) + { + EditorGUILayout.HelpBox(s_Styles.warpNotSupportWarning.text, MessageType.Warning, true); + } + } + + // Filter mode + EditorGUI.showMixedValue = m_FilterMode.hasMultipleDifferentValues; + FilterMode filter = (FilterMode)m_FilterMode.intValue; + if ((int)filter == -1) + { + if (m_FadeOut.intValue > 0 || m_ConvertToNormalMap.intValue > 0) + filter = FilterMode.Trilinear; + else + filter = FilterMode.Bilinear; + } + filter = (FilterMode)EditorGUILayout.IntPopup(s_Styles.filterMode, (int)filter, s_Styles.filterModeOptions, m_FilterModeOptions); + EditorGUI.showMixedValue = false; + if (EditorGUI.EndChangeCheck()) + m_FilterMode.intValue = (int)filter; + + // Aniso + bool showAniso = (FilterMode)m_FilterMode.intValue != FilterMode.Point + && m_EnableMipMap.intValue > 0 + && (TextureImporterShape)m_TextureShape.intValue != TextureImporterShape.TextureCube; + using (new EditorGUI.DisabledScope(!showAniso)) + { + EditorGUI.BeginChangeCheck(); + EditorGUI.showMixedValue = m_Aniso.hasMultipleDifferentValues; + int aniso = m_Aniso.intValue; + if (aniso == -1) + aniso = 1; + aniso = EditorGUILayout.IntSlider(s_Styles.anisoLevelLabel, aniso, 0, 16); + EditorGUI.showMixedValue = false; + if (EditorGUI.EndChangeCheck()) + m_Aniso.intValue = aniso; + + if (aniso > 1) + { + if (QualitySettings.anisotropicFiltering == AnisotropicFiltering.Disable) + EditorGUILayout.HelpBox(s_Styles.anisotropicDisableInfo.text, MessageType.Info); + else if (QualitySettings.anisotropicFiltering == AnisotropicFiltering.ForceEnable) + EditorGUILayout.HelpBox(s_Styles.anisotropicForceEnableInfo.text, MessageType.Info); + } + } + GUILayout.Space(5); + } + } + + private static bool IsAnyTextureObjectUsingPerAxisWrapMode(UnityEngine.Object[] objects, bool isVolumeTexture) + { + foreach (var o in objects) + { + int u = 0, v = 0, w = 0; + // the objects can be Textures themselves, or texture-related importers + if (o is Texture) + { + var ti = (Texture)o; + u = (int)ti.wrapModeU; + v = (int)ti.wrapModeV; + w = (int)ti.wrapModeW; + } + if (o is TextureImporter) + { + var ti = (TextureImporter)o; + u = (int)ti.wrapModeU; + v = (int)ti.wrapModeV; + w = (int)ti.wrapModeW; + } + if (o is IHVImageFormatImporter) + { + var ti = (IHVImageFormatImporter)o; + u = (int)ti.wrapModeU; + v = (int)ti.wrapModeV; + w = (int)ti.wrapModeW; + } + u = Mathf.Max(0, u); + v = Mathf.Max(0, v); + w = Mathf.Max(0, w); + if (u != v) + { + return true; + } + if (isVolumeTexture) + { + if (u != w || v != w) + { + return true; + } + } + } + return false; + } + + // showPerAxisWrapModes is state of whether "Per-Axis" mode should be active in the main dropdown. + // It is set automatically if wrap modes in UVW are different, or if user explicitly picks "Per-Axis" option -- when that one is picked, + // then it should stay true even if UVW wrap modes will initially be the same. + // + // Note: W wrapping mode is only shown when isVolumeTexture is true. + internal static void WrapModePopup(SerializedProperty wrapU, SerializedProperty wrapV, SerializedProperty wrapW, bool isVolumeTexture, ref bool showPerAxisWrapModes) + { + if (s_Styles == null) + s_Styles = new Styles(); + + // In texture importer settings, serialized properties for things like wrap modes can contain -1; + // that seems to indicate "use defaults, user has not changed them to anything" but not totally sure. + // Show them as Repeat wrap modes in the popups. + var wu = (TextureWrapMode)Mathf.Max(wrapU.intValue, 0); + var wv = (TextureWrapMode)Mathf.Max(wrapV.intValue, 0); + var ww = (TextureWrapMode)Mathf.Max(wrapW.intValue, 0); + + // automatically go into per-axis mode if values are already different + if (wu != wv) + showPerAxisWrapModes = true; + if (isVolumeTexture) + { + if (wu != ww || wv != ww) + showPerAxisWrapModes = true; + } + + // It's not possible to determine whether any single texture in the whole selection is using per-axis wrap modes + // just from SerializedProperty values. They can only tell if "some values in whole selection are different" (e.g. + // wrap value on U axis is not the same among all textures), and can return value of "some" object in the selection + // (typically based on object loading order). So in order for more intuitive behavior with multi-selection, + // we go over the actual objects when there's >1 object selected and some wrap modes are different. + if (!showPerAxisWrapModes) + { + if (wrapU.hasMultipleDifferentValues || wrapV.hasMultipleDifferentValues || (isVolumeTexture && wrapW.hasMultipleDifferentValues)) + { + if (IsAnyTextureObjectUsingPerAxisWrapMode(wrapU.serializedObject.targetObjects, isVolumeTexture)) + { + showPerAxisWrapModes = true; + } + } + } + + int value = showPerAxisWrapModes ? -1 : (int)wu; + + // main wrap mode popup + EditorGUI.BeginChangeCheck(); + EditorGUI.showMixedValue = !showPerAxisWrapModes && (wrapU.hasMultipleDifferentValues || wrapV.hasMultipleDifferentValues || (isVolumeTexture && wrapW.hasMultipleDifferentValues)); + value = EditorGUILayout.IntPopup(s_Styles.wrapModeLabel, value, s_Styles.wrapModeContents, s_Styles.wrapModeValues); + if (EditorGUI.EndChangeCheck() && value != -1) + { + // assign the same wrap mode to all axes, and hide per-axis popups + wrapU.intValue = value; + wrapV.intValue = value; + wrapW.intValue = value; + showPerAxisWrapModes = false; + } + + // show per-axis popups if needed + if (value == -1) + { + showPerAxisWrapModes = true; + EditorGUI.indentLevel++; + WrapModeAxisPopup(s_Styles.wrapU, wrapU); + WrapModeAxisPopup(s_Styles.wrapV, wrapV); + if (isVolumeTexture) + { + WrapModeAxisPopup(s_Styles.wrapW, wrapW); + } + EditorGUI.indentLevel--; + } + EditorGUI.showMixedValue = false; + } + + static void WrapModeAxisPopup(GUIContent label, SerializedProperty wrapProperty) + { + // In texture importer settings, serialized properties for wrap modes can contain -1, which means "use default". + var wrap = (TextureWrapMode)Mathf.Max(wrapProperty.intValue, 0); + Rect rect = EditorGUILayout.GetControlRect(); + EditorGUI.BeginChangeCheck(); + EditorGUI.BeginProperty(rect, label, wrapProperty); + wrap = (TextureWrapMode)EditorGUI.EnumPopup(rect, label, wrap); + EditorGUI.EndProperty(); + if (EditorGUI.EndChangeCheck()) + { + wrapProperty.intValue = (int)wrap; + } + } + + void DoWrapModePopup() + { + WrapModePopup(m_WrapU, m_WrapV, m_WrapW, IsVolume(), ref m_ShowPerAxisWrapModes); + } + + bool IsVolume() + { + var t = target as Texture; + return t != null && t.dimension == UnityEngine.Rendering.TextureDimension.Tex3D; + } + + void DoSpriteTextureTypeInspector() + { + EditorGUI.BeginChangeCheck(); + EditorGUILayout.IntPopup(m_SpriteMode, s_Styles.spriteModeOptions, new[] { 1, 2, 3 }, s_Styles.spriteMode); + + // Ensure that PropertyField focus will be cleared when we change spriteMode. + if (EditorGUI.EndChangeCheck()) + { + GUIUtility.keyboardControl = 0; + } + + // Show generic attributes + using (new EditorGUI.DisabledScope(m_SpriteMode.intValue == 0)) + { + EditorGUILayout.PropertyField(m_SpritePixelsToUnits, s_Styles.spritePixelsPerUnit); + + if (m_SpriteMode.intValue != (int)SpriteImportMode.Polygon && !m_SpriteMode.hasMultipleDifferentValues) + { + EditorGUILayout.IntPopup(m_SpriteMeshType, s_Styles.spriteMeshTypeOptions, new[] { 0, 1 }, s_Styles.spriteMeshType); + } + + EditorGUILayout.IntSlider(m_SpriteExtrude, 0, 32, s_Styles.spriteExtrude); + + if (m_SpriteMode.intValue == 1) + { + EditorGUILayout.IntPopup(m_Alignment, s_Styles.spriteAlignmentOptions, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, s_Styles.spriteAlignment); + + if (m_Alignment.intValue == (int)SpriteAlignment.Custom) + { + GUILayout.BeginHorizontal(); + EditorGUILayout.PropertyField(m_SpritePivot, new GUIContent()); + GUILayout.EndHorizontal(); + } + } + } + EditorGUILayout.PropertyField(m_GeneratePhysicsShape, s_Styles.generatePhysicsShape); + using (new EditorGUI.DisabledScope(!m_MosaicLayers.boolValue)) + { + EditorGUILayout.PropertyField(m_ResliceFromLayer, s_Styles.resliceFromLayer); + if (m_ResliceFromLayer.boolValue) + { + EditorGUILayout.HelpBox(s_Styles.resliceFromLayerWarning.text, MessageType.Info, true); + } + } + + DoOpenSpriteEditorButton(); + } + + void DoSpriteInspector() + { + if (m_EditorFoldOutState.DoLayerImportUI(s_Styles.layerImportHeaderText)) + { + using (new EditorGUI.DisabledScope(m_SpriteMode.intValue != (int)SpriteImportMode.Multiple || m_SpriteMode.hasMultipleDifferentValues)) + { + EditorGUILayout.PropertyField(m_ImportHiddenLayers, s_Styles.importHiddenLayer); + + using (new EditorGUI.DisabledScope(m_SpriteMode.intValue != (int)SpriteImportMode.Multiple || m_SpriteMode.hasMultipleDifferentValues)) + { + using (new EditorGUI.DisabledScope(m_MosaicLayers.boolValue == false)) + { + EditorGUILayout.PropertyField(m_KeepDupilcateSpriteName, s_Styles.keepDuplicateSpriteName); + + using (new EditorGUI.DisabledScope(!m_MosaicLayers.boolValue || !m_CharacterMode.boolValue)) + { + EditorGUILayout.PropertyField(m_GenerateGOHierarchy, s_Styles.layerGroupLabel); + } + + EditorGUILayout.IntPopup(m_LayerMappingOption, s_Styles.layerMappingOption, s_Styles.layerMappingOptionValues, s_Styles.layerMapping); + } + + var boolToInt = !m_MosaicLayers.boolValue ? 1 : 0; + EditorGUI.BeginChangeCheck(); + boolToInt = EditorGUILayout.IntPopup(s_Styles.mosaicLayers, boolToInt, s_Styles.importModeOptions, s_Styles.falseTrueOptionValue); + if (EditorGUI.EndChangeCheck()) + m_MosaicLayers.boolValue = boolToInt != 1; + } + GUILayout.Space(5); + } + GUILayout.Space(5); + } + + if (m_EditorFoldOutState.DoCharacterRigUI(s_Styles.characterRigHeaderText)) + { + using (new EditorGUI.DisabledScope(m_SpriteMode.intValue != (int)SpriteImportMode.Multiple || m_SpriteMode.hasMultipleDifferentValues || m_MosaicLayers.boolValue == false)) + { + EditorGUILayout.PropertyField(m_CharacterMode, s_Styles.characterMode); + using (new EditorGUI.DisabledScope(!m_CharacterMode.boolValue)) + { + MainRigPropertyField(); + EditorGUILayout.IntPopup(m_DocumentAlignment, s_Styles.spriteAlignmentOptions, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, s_Styles.characterAlignment); + if (m_DocumentAlignment.intValue == (int)SpriteAlignment.Custom) + { + GUILayout.BeginHorizontal(); + GUILayout.Space(EditorGUIUtility.labelWidth); + EditorGUILayout.PropertyField(m_DocumentPivot, new GUIContent()); + GUILayout.EndHorizontal(); + } + } + } + GUILayout.Space(5); + //EditorGUILayout.PropertyField(m_PaperDollMode, s_Styles.paperDollMode); + } + } + + void DoOpenSpriteEditorButton() + { + using (new EditorGUI.DisabledScope(targets.Length != 1)) + { + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + if (GUILayout.Button(s_Styles.spriteEditorButtonLabel)) + { + if (HasModified()) + { + // To ensure Sprite Editor Window to have the latest texture import setting, + // We must applied those modified values first. + string dialogText = string.Format(s_Styles.unappliedSettingsDialogContent.text, ((AssetImporter)target).assetPath); + if (EditorUtility.DisplayDialog(s_Styles.unappliedSettingsDialogTitle.text, + dialogText, s_Styles.applyButtonLabel.text, s_Styles.cancelButtonLabel.text)) + { + ApplyAndImport(); + InternalEditorBridge.ShowSpriteEditorWindow(this.assetTarget); + + // We reimported the asset which destroyed the editor, so we can't keep running the UI here. + GUIUtility.ExitGUI(); + } + } + else + { + InternalEditorBridge.ShowSpriteEditorWindow(this.assetTarget); + } + } + GUILayout.EndHorizontal(); + } + } + + void DoTextureDefaultTextureTypeInspector() + { + ColorSpaceGUI(); + AlphaHandlingGUI(); + } + + void ColorSpaceGUI() + { + ToggleFromInt(m_sRGBTexture, s_Styles.sRGBTexture); + } + + void POTScaleGUI() + { + using (new EditorGUI.DisabledScope(m_IsPOT || m_TextureType.intValue == (int)TextureImporterType.Sprite)) + { + EnumPopup(m_NPOTScale, typeof(TextureImporterNPOTScale), s_Styles.npot); + } + } + + void ReadableGUI() + { + ToggleFromInt(m_IsReadable, s_Styles.readWrite); + } + + void AlphaHandlingGUI() + { + EditorGUI.showMixedValue = m_AlphaSource.hasMultipleDifferentValues; + EditorGUI.BeginChangeCheck(); + int newAlphaUsage = EditorGUILayout.IntPopup(s_Styles.alphaSource, m_AlphaSource.intValue, s_Styles.alphaSourceOptions, s_Styles.alphaSourceValues); + + EditorGUI.showMixedValue = false; + if (EditorGUI.EndChangeCheck()) + { + m_AlphaSource.intValue = newAlphaUsage; + } + + bool showAlphaIsTransparency = (TextureImporterAlphaSource)m_AlphaSource.intValue != TextureImporterAlphaSource.None; + using (new EditorGUI.DisabledScope(!showAlphaIsTransparency)) + { + ToggleFromInt(m_AlphaIsTransparency, s_Styles.alphaIsTransparency); + } + } + + void MipMapGUI() + { + ToggleFromInt(m_EnableMipMap, s_Styles.generateMipMaps); + + if (m_EnableMipMap.boolValue && !m_EnableMipMap.hasMultipleDifferentValues) + { + EditorGUI.indentLevel++; + ToggleFromInt(m_BorderMipMap, s_Styles.borderMipMaps); + m_MipMapMode.intValue = EditorGUILayout.Popup(s_Styles.mipMapFilter, m_MipMapMode.intValue, s_Styles.mipMapFilterOptions); + + ToggleFromInt(m_MipMapsPreserveCoverage, s_Styles.mipMapsPreserveCoverage); + if (m_MipMapsPreserveCoverage.intValue != 0 && !m_MipMapsPreserveCoverage.hasMultipleDifferentValues) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_AlphaTestReferenceValue, s_Styles.alphaTestReferenceValue); + EditorGUI.indentLevel--; + } + + // Mipmap fadeout + ToggleFromInt(m_FadeOut, s_Styles.mipmapFadeOutToggle); + if (m_FadeOut.intValue > 0) + { + EditorGUI.indentLevel++; + EditorGUI.BeginChangeCheck(); + float min = m_MipMapFadeDistanceStart.intValue; + float max = m_MipMapFadeDistanceEnd.intValue; + EditorGUILayout.MinMaxSlider(s_Styles.mipmapFadeOut, ref min, ref max, 0, 10); + if (EditorGUI.EndChangeCheck()) + { + m_MipMapFadeDistanceStart.intValue = Mathf.RoundToInt(min); + m_MipMapFadeDistanceEnd.intValue = Mathf.RoundToInt(max); + } + EditorGUI.indentLevel--; + } + EditorGUI.indentLevel--; + } + } + + void ToggleFromInt(SerializedProperty property, GUIContent label) + { + EditorGUI.BeginChangeCheck(); + EditorGUI.showMixedValue = property.hasMultipleDifferentValues; + int value = EditorGUILayout.Toggle(label, property.intValue > 0) ? 1 : 0; + EditorGUI.showMixedValue = false; + if (EditorGUI.EndChangeCheck()) + property.intValue = value; + } + + void EnumPopup(SerializedProperty property, System.Type type, GUIContent label) + { + EditorGUILayout.IntPopup(label.text, property.intValue, + System.Enum.GetNames(type), + System.Enum.GetValues(type) as int[]); + } + + void ExportMosaicTexture() + { + var assetPath = ((AssetImporter)target).assetPath; + var texture2D = AssetDatabase.LoadAssetAtPath(assetPath); + if (texture2D == null) + return; + if (!texture2D.isReadable) + texture2D = InternalEditorBridge.CreateTemporaryDuplicate(texture2D, texture2D.width, texture2D.height); + var pixelData = texture2D.GetPixels(); + texture2D = new Texture2D(texture2D.width, texture2D.height); + texture2D.SetPixels(pixelData); + texture2D.Apply(); + byte[] bytes = texture2D.EncodeToPNG(); + var fileName = Path.GetFileNameWithoutExtension(assetPath); + var filePath = Path.GetDirectoryName(assetPath); + var savePath = Path.Combine(filePath, fileName + ".png"); + File.WriteAllBytes(savePath, bytes); + AssetDatabase.Refresh(); + } + + /// + /// Implementation of AssetImporterEditor.ResetValues. + /// + protected override void ResetValues() + { + base.ResetValues(); + LoadPlatformSettings(); + m_TexturePlatformSettingsHelper = new TexturePlatformSettingsHelper(this); + } + + /// + /// Implementation of ITexturePlatformSettingsDataProvider.GetTargetCount. + /// + /// Returns the number of selected targets. + public int GetTargetCount() + { + return targets.Length; + } + + /// + /// ITexturePlatformSettingsDataProvider.GetPlatformTextureSettings. + /// + /// Selected target index. + /// Name of the platform. + /// TextureImporterPlatformSettings for the given platform name and selected target index. + public TextureImporterPlatformSettings GetPlatformTextureSettings(int i, string name) + { + if(m_PlatfromSettings.ContainsKey(name)) + if(m_PlatfromSettings[name].Count > i) + return m_PlatfromSettings[name][i]; + return new TextureImporterPlatformSettings() + { + name = name, + overridden = false + }; + } + + /// + /// Implementation of ITexturePlatformSettingsDataProvider.ShowPresetSettings. + /// + /// True if valid asset is selected, false otherwiser. + public bool ShowPresetSettings() + { + return assetTarget == null; + } + + /// + /// Implementation of ITexturePlatformSettingsDataProvider.DoesSourceTextureHaveAlpha. + /// + /// Index to selected target. + /// Always returns true since importer deals with source file that has alpha. + public bool DoesSourceTextureHaveAlpha(int i) + { + return true; + } + + /// + /// Implementation of ITexturePlatformSettingsDataProvider.IsSourceTextureHDR. + /// + /// Index to selected target. + /// Always returns false since importer does not handle HDR textures. + public bool IsSourceTextureHDR(int i) + { + return false; + } + + /// + /// Implementation of ITexturePlatformSettingsDataProvider.SetPlatformTextureSettings. + /// + /// Selected target index. + /// TextureImporterPlatformSettings to apply to target. + public void SetPlatformTextureSettings(int i, TextureImporterPlatformSettings platformSettings) + { + var psdImporter = ((PSDImporter)targets[i]); + psdImporter.SetPlatformTextureSettings(platformSettings); + psdImporter.Apply(); + } + + /// + /// Implementation of ITexturePlatformSettingsDataProvider.GetImporterSettings. + /// + /// Selected target index. + /// TextureImporterPlatformSettings reference for data retrieval. + public void GetImporterSettings(int i, TextureImporterSettings settings) + { + ((PSDImporter)targets[i]).ReadTextureSettings(settings); + // Get settings that have been changed in the inspector + GetSerializedPropertySettings(settings); + } + + internal TextureImporterSettings GetSerializedPropertySettings(TextureImporterSettings settings) + { + if (!m_AlphaSource.hasMultipleDifferentValues) + settings.alphaSource = (TextureImporterAlphaSource)m_AlphaSource.intValue; + + if (!m_ConvertToNormalMap.hasMultipleDifferentValues) + settings.convertToNormalMap = m_ConvertToNormalMap.intValue > 0; + + if (!m_BorderMipMap.hasMultipleDifferentValues) + settings.borderMipmap = m_BorderMipMap.intValue > 0; + + if (!m_MipMapsPreserveCoverage.hasMultipleDifferentValues) + settings.mipMapsPreserveCoverage = m_MipMapsPreserveCoverage.intValue > 0; + + if (!m_AlphaTestReferenceValue.hasMultipleDifferentValues) + settings.alphaTestReferenceValue = m_AlphaTestReferenceValue.floatValue; + + if (!m_NPOTScale.hasMultipleDifferentValues) + settings.npotScale = (TextureImporterNPOTScale)m_NPOTScale.intValue; + + if (!m_IsReadable.hasMultipleDifferentValues) + settings.readable = m_IsReadable.intValue > 0; + + if (!m_sRGBTexture.hasMultipleDifferentValues) + settings.sRGBTexture = m_sRGBTexture.intValue > 0; + + if (!m_EnableMipMap.hasMultipleDifferentValues) + settings.mipmapEnabled = m_EnableMipMap.intValue > 0; + + if (!m_MipMapMode.hasMultipleDifferentValues) + settings.mipmapFilter = (TextureImporterMipFilter)m_MipMapMode.intValue; + + if (!m_FadeOut.hasMultipleDifferentValues) + settings.fadeOut = m_FadeOut.intValue > 0; + + if (!m_MipMapFadeDistanceStart.hasMultipleDifferentValues) + settings.mipmapFadeDistanceStart = m_MipMapFadeDistanceStart.intValue; + + if (!m_MipMapFadeDistanceEnd.hasMultipleDifferentValues) + settings.mipmapFadeDistanceEnd = m_MipMapFadeDistanceEnd.intValue; + + if (!m_SpriteMode.hasMultipleDifferentValues) + settings.spriteMode = m_SpriteMode.intValue; + + if (!m_SpritePixelsToUnits.hasMultipleDifferentValues) + settings.spritePixelsPerUnit = m_SpritePixelsToUnits.floatValue; + + if (!m_SpriteExtrude.hasMultipleDifferentValues) + settings.spriteExtrude = (uint)m_SpriteExtrude.intValue; + + if (!m_SpriteMeshType.hasMultipleDifferentValues) + settings.spriteMeshType = (SpriteMeshType)m_SpriteMeshType.intValue; + + if (!m_Alignment.hasMultipleDifferentValues) + settings.spriteAlignment = m_Alignment.intValue; + + if (!m_SpritePivot.hasMultipleDifferentValues) + settings.spritePivot = m_SpritePivot.vector2Value; + + if (!m_WrapU.hasMultipleDifferentValues) + settings.wrapModeU = (TextureWrapMode)m_WrapU.intValue; + if (!m_WrapV.hasMultipleDifferentValues) + settings.wrapModeU = (TextureWrapMode)m_WrapV.intValue; + if (!m_WrapW.hasMultipleDifferentValues) + settings.wrapModeU = (TextureWrapMode)m_WrapW.intValue; + + if (!m_FilterMode.hasMultipleDifferentValues) + settings.filterMode = (FilterMode)m_FilterMode.intValue; + + if (!m_Aniso.hasMultipleDifferentValues) + settings.aniso = m_Aniso.intValue; + + + if (!m_AlphaIsTransparency.hasMultipleDifferentValues) + settings.alphaIsTransparency = m_AlphaIsTransparency.intValue > 0; + + if (!m_TextureType.hasMultipleDifferentValues) + settings.textureType = (TextureImporterType)m_TextureType.intValue; + + if (!m_TextureShape.hasMultipleDifferentValues) + settings.textureShape = (TextureImporterShape)m_TextureShape.intValue; + + return settings; + } + /// + /// Override of AssetImporterEditor.showImportedObject + /// The property always returns false so that imported objects does not show up in the Inspector. + /// + /// false + public override bool showImportedObject + { + get { return false; } + } + + /// + /// Implementation of ITexturePlatformSettingsDataProvider.textureTypeHasMultipleDifferentValues. + /// + public bool textureTypeHasMultipleDifferentValues + { + get { return m_TextureType.hasMultipleDifferentValues; } + } + + + /// + /// Implementation of ITexturePlatformSettingsDataProvider.textureType. + /// + public TextureImporterType textureType + { + get { return (TextureImporterType)m_TextureType.intValue; } + } + + + /// + /// Implementation of ITexturePlatformSettingsDataProvider.spriteImportMode. + /// + public SpriteImportMode spriteImportMode + { + get { return (SpriteImportMode)m_SpriteMode.intValue; } + } + + /// + /// Override of AssetImporterEditor.HasModified. + /// + /// Returns True if has modified data. False otherwise. + public override bool HasModified() + { + if (base.HasModified()) + return true; + + return m_TexturePlatformSettingsHelper.HasModified(); + } + + internal class Styles + { + public readonly GUIContent textureTypeTitle = new GUIContent("Texture Type", "What will this texture be used for?"); + public readonly GUIContent[] textureTypeOptions = + { + new GUIContent("Default", "Texture is a normal image such as a diffuse texture or other."), + new GUIContent("Sprite (2D and UI)", "Texture is used for a sprite."), + }; + public readonly int[] textureTypeValues = + { + (int)TextureImporterType.Default, + (int)TextureImporterType.Sprite, + }; + + private readonly GUIContent textureShape2D = new GUIContent("2D, Texture is 2D."); + private readonly GUIContent textureShapeCube = new GUIContent("Cube", "Texture is a Cubemap."); + public readonly Dictionary textureShapeOptionsDictionnary = new Dictionary(); + public readonly Dictionary textureShapeValuesDictionnary = new Dictionary(); + + + public readonly GUIContent filterMode = new GUIContent("Filter Mode"); + public readonly GUIContent[] filterModeOptions = + { + new GUIContent("Point (no filter)"), + new GUIContent("Bilinear"), + new GUIContent("Trilinear") + }; + + public readonly GUIContent mipmapFadeOutToggle = new GUIContent("Fadeout Mip Maps"); + public readonly GUIContent mipmapFadeOut = new GUIContent("Fade Range"); + public readonly GUIContent readWrite = new GUIContent("Read/Write Enabled", "Enable to be able to access the raw pixel data from code."); + + public readonly GUIContent alphaSource = new GUIContent("Alpha Source", "How is the alpha generated for the imported texture."); + public readonly GUIContent[] alphaSourceOptions = + { + new GUIContent("None", "No Alpha will be used."), + new GUIContent("Input Texture Alpha", "Use Alpha from the input texture if one is provided."), + new GUIContent("From Gray Scale", "Generate Alpha from image gray scale."), + }; + public readonly int[] alphaSourceValues = + { + (int)TextureImporterAlphaSource.None, + (int)TextureImporterAlphaSource.FromInput, + (int)TextureImporterAlphaSource.FromGrayScale, + }; + + public readonly GUIContent generateMipMaps = new GUIContent("Generate Mip Maps"); + public readonly GUIContent sRGBTexture = new GUIContent("sRGB (Color Texture)", "Texture content is stored in gamma space. Non-HDR color textures should enable this flag (except if used for IMGUI)."); + public readonly GUIContent borderMipMaps = new GUIContent("Border Mip Maps"); + public readonly GUIContent mipMapsPreserveCoverage = new GUIContent("Mip Maps Preserve Coverage", "The alpha channel of generated Mip Maps will preserve coverage during the alpha test."); + public readonly GUIContent alphaTestReferenceValue = new GUIContent("Alpha Cutoff Value", "The reference value used during the alpha test. Controls Mip Map coverage."); + public readonly GUIContent mipMapFilter = new GUIContent("Mip Map Filtering"); + public readonly GUIContent[] mipMapFilterOptions = + { + new GUIContent("Box"), + new GUIContent("Kaiser"), + }; + public readonly GUIContent npot = new GUIContent("Non Power of 2", "How non-power-of-two textures are scaled on import."); + + public readonly GUIContent spriteMode = new GUIContent("Sprite Mode"); + public readonly GUIContent[] spriteModeOptions = + { + new GUIContent("Single"), + new GUIContent("Multiple"), + new GUIContent("Polygon"), + }; + public readonly GUIContent[] spriteMeshTypeOptions = + { + new GUIContent("Full Rect"), + new GUIContent("Tight"), + }; + + public readonly GUIContent spritePixelsPerUnit = new GUIContent("Pixels Per Unit", "How many pixels in the sprite correspond to one unit in the world."); + public readonly GUIContent spriteExtrude = new GUIContent("Extrude Edges", "How much empty area to leave around the sprite in the generated mesh."); + public readonly GUIContent spriteMeshType = new GUIContent("Mesh Type", "Type of sprite mesh to generate."); + public readonly GUIContent spriteAlignment = new GUIContent("Pivot", "Sprite pivot point in its local space. May be used for syncing animation frames of different sizes."); + public readonly GUIContent characterAlignment = new GUIContent("Pivot", "Character pivot point in its local space using normalized value i.e. 0 - 1"); + + public readonly GUIContent[] spriteAlignmentOptions = + { + new GUIContent("Center"), + new GUIContent("Top Left"), + new GUIContent("Top"), + new GUIContent("Top Right"), + new GUIContent("Left"), + new GUIContent("Right"), + new GUIContent("Bottom Left"), + new GUIContent("Bottom"), + new GUIContent("Bottom Right"), + new GUIContent("Custom"), + }; + + public readonly GUIContent warpNotSupportWarning = new GUIContent("Graphics device doesn't support Repeat wrap mode on NPOT textures. Falling back to Clamp."); + public readonly GUIContent anisoLevelLabel = new GUIContent("Aniso Level"); + public readonly GUIContent anisotropicDisableInfo = new GUIContent("Anisotropic filtering is disabled for all textures in Quality Settings."); + public readonly GUIContent anisotropicForceEnableInfo = new GUIContent("Anisotropic filtering is enabled for all textures in Quality Settings."); + public readonly GUIContent unappliedSettingsDialogTitle = new GUIContent("Unapplied import settings"); + public readonly GUIContent unappliedSettingsDialogContent = new GUIContent("Unapplied import settings for \'{0}\'.\nApply and continue to sprite editor or cancel."); + public readonly GUIContent applyButtonLabel = new GUIContent("Apply"); + public readonly GUIContent cancelButtonLabel = new GUIContent("Cancel"); + public readonly GUIContent spriteEditorButtonLabel = new GUIContent("Open Sprite Editor"); + public readonly GUIContent resliceFromLayerWarning = new GUIContent("This will reinitialize and recreate all Sprites based on the file’s layer data. Existing Sprite metadata from previously generated Sprites are copied over."); + public readonly GUIContent alphaIsTransparency = new GUIContent("Alpha Is Transparency", "If the provided alpha channel is transparency, enable this to pre-filter the color to avoid texture filtering artifacts. This is not supported for HDR textures."); + + public readonly GUIContent advancedHeaderText = new GUIContent("Advanced", "Show advanced settings."); + + public readonly GUIContent platformSettingsHeaderText = new GUIContent("Platform Setttings"); + + public readonly GUIContent[] platformSettingsSelection; + + public readonly GUIContent wrapModeLabel = new GUIContent("Wrap Mode"); + public readonly GUIContent wrapU = new GUIContent("U axis"); + public readonly GUIContent wrapV = new GUIContent("V axis"); + public readonly GUIContent wrapW = new GUIContent("W axis"); + + + public readonly GUIContent[] wrapModeContents = + { + new GUIContent("Repeat"), + new GUIContent("Clamp"), + new GUIContent("Mirror"), + new GUIContent("Mirror Once"), + new GUIContent("Per-axis") + }; + public readonly int[] wrapModeValues = + { + (int)TextureWrapMode.Repeat, + (int)TextureWrapMode.Clamp, + (int)TextureWrapMode.Mirror, + (int)TextureWrapMode.MirrorOnce, + -1 + }; + + public readonly GUIContent layerMapping = EditorGUIUtility.TrTextContent("Layer Mapping", "Options for indicating how layer to Sprite mapping."); + public readonly GUIContent generatePhysicsShape = EditorGUIUtility.TrTextContent("Generate Physics Shape", "Generates a default physics shape from the outline of the Sprite/s when a physics shape has not been set in the Sprite Editor."); + public readonly GUIContent importHiddenLayer = EditorGUIUtility.TrTextContent("Include Hidden Layers", "Settings to determine when hidden layers should be imported."); + public readonly GUIContent mosaicLayers = EditorGUIUtility.TrTextContent("Import Mode", "Layers will be imported as individual Sprites."); + public readonly GUIContent characterMode = EditorGUIUtility.TrTextContent("Use as Rig","Enable to support 2D Animation character rigging."); + public readonly GUIContent layerGroupLabel = EditorGUIUtility.TrTextContent("Use Layer Group", "GameObjects are grouped according to source file layer grouping."); + public readonly GUIContent resliceFromLayer = EditorGUIUtility.TrTextContent("Automatic Reslice", "Recreate Sprite rects from file."); + public readonly GUIContent paperDollMode = EditorGUIUtility.TrTextContent("Paper Doll Mode", "Special mode to generate a Prefab for Paper Doll use case."); + public readonly GUIContent keepDuplicateSpriteName = EditorGUIUtility.TrTextContent("Keep Duplicate Names", "Keep Sprite name same as Layer Name even if there are duplicated Layer Name."); + public readonly GUIContent mainSkeletonName = EditorGUIUtility.TrTextContent("Main Skeleton", "Main Skeleton to use for Rigging."); + public readonly GUIContent generalHeaderText = EditorGUIUtility.TrTextContent("General", "General settings."); + public readonly GUIContent layerImportHeaderText = EditorGUIUtility.TrTextContent("Layer Import","Layer Import settings."); + public readonly GUIContent characterRigHeaderText = EditorGUIUtility.TrTextContent("Character Rig","Character Rig settings."); + public readonly GUIContent textureHeaderText = EditorGUIUtility.TrTextContent("Texture","Texture settings."); + + + public readonly int[] falseTrueOptionValue = + { + 0, + 1 + }; + + public readonly GUIContent[] importModeOptions= + { + EditorGUIUtility.TrTextContent("Individual Sprites (Mosaic)","Import individual Photoshop layers as Sprites."), + new GUIContent("Merged","Merge Photoshop layers and import as single Sprite.") + }; + + public readonly GUIContent[] layerMappingOption= + { + EditorGUIUtility.TrTextContent("Use Layer ID","Use layer's ID to identify layer and Sprite mapping."), + EditorGUIUtility.TrTextContent("Use Layer Name","Use layer's name to identify layer and Sprite mapping."), + EditorGUIUtility.TrTextContent("Use Layer Name (Case Sensitive)","Use layer's name to identify layer and Sprite mapping."), + }; + + public readonly int[] layerMappingOptionValues = + { + (int)PSDImporter.ELayerMappingOption.UseLayerId, + (int)PSDImporter.ELayerMappingOption.UseLayerName, + (int)PSDImporter.ELayerMappingOption.UseLayerNameCaseSensitive + }; + + public readonly GUIContent[] editorTabNames = + { + EditorGUIUtility.TrTextContent("Settings", "Importer Settings."), + EditorGUIUtility.TrTextContent("Layer Management", "Layer merge settings.") + }; + + public Styles() + { + // This is far from ideal, but it's better than having tons of logic in the GUI code itself. + // The combination should not grow too much anyway since only Texture3D will be added later. + GUIContent[] s2D_Options = { textureShape2D }; + GUIContent[] sCube_Options = { textureShapeCube }; + GUIContent[] s2D_Cube_Options = { textureShape2D, textureShapeCube }; + textureShapeOptionsDictionnary.Add(TextureImporterShape.Texture2D, s2D_Options); + textureShapeOptionsDictionnary.Add(TextureImporterShape.TextureCube, sCube_Options); + textureShapeOptionsDictionnary.Add(TextureImporterShape.Texture2D | TextureImporterShape.TextureCube, s2D_Cube_Options); + + int[] s2D_Values = { (int)TextureImporterShape.Texture2D }; + int[] sCube_Values = { (int)TextureImporterShape.TextureCube }; + int[] s2D_Cube_Values = { (int)TextureImporterShape.Texture2D, (int)TextureImporterShape.TextureCube }; + textureShapeValuesDictionnary.Add(TextureImporterShape.Texture2D, s2D_Values); + textureShapeValuesDictionnary.Add(TextureImporterShape.TextureCube, sCube_Values); + textureShapeValuesDictionnary.Add(TextureImporterShape.Texture2D | TextureImporterShape.TextureCube, s2D_Cube_Values); + + platformSettingsSelection = new GUIContent[TexturePlatformSettingsModal.kValidBuildPlatform.Length]; + for (int i = 0; i < TexturePlatformSettingsModal.kValidBuildPlatform.Length; ++i) + { + platformSettingsSelection[i] = new GUIContent(TexturePlatformSettingsModal.kValidBuildPlatform[i].buildTargetName); + } + } + } + + internal static Styles s_Styles; + } + + class PSDImporterEditorFoldOutState + { + SavedBool m_GeneralFoldout; + SavedBool m_LayerImportFoldout; + SavedBool m_CharacterRigFoldout; + SavedBool m_AdvancedFoldout; + SavedBool m_TextureFoldout; + SavedBool m_PlatformSettingsFoldout; + + public PSDImporterEditorFoldOutState() + { + m_GeneralFoldout = new SavedBool("PSDImporterEditor.m_GeneralFoldout", true); + m_LayerImportFoldout = new SavedBool("PSDImporterEditor.m_LayerImportFoldout", true); + m_CharacterRigFoldout = new SavedBool("PSDImporterEditor.m_CharacterRigFoldout", false); + m_AdvancedFoldout = new SavedBool("PSDImporterEditor.m_AdvancedFoldout", false); + m_TextureFoldout = new SavedBool("PSDImporterEditor.m_TextureFoldout", false); + m_PlatformSettingsFoldout = new SavedBool("PSDImporterEditor.m_PlatformSettingsFoldout", false); + } + + bool DoFoldout(GUIContent title, bool state) + { + InspectorUtils.DrawSplitter(); + return InspectorUtils.DrawHeaderFoldout(title, state); + } + + public bool DoGeneralUI(GUIContent title) + { + m_GeneralFoldout.value = DoFoldout(title, m_GeneralFoldout.value); + return m_GeneralFoldout.value; + } + + public bool DoLayerImportUI(GUIContent title) + { + m_LayerImportFoldout.value = DoFoldout(title, m_LayerImportFoldout.value); + return m_LayerImportFoldout.value; + } + + public bool DoCharacterRigUI(GUIContent title) + { + m_CharacterRigFoldout.value = DoFoldout(title, m_CharacterRigFoldout.value); + return m_CharacterRigFoldout.value; + } + + public bool DoAdvancedUI(GUIContent title) + { + m_AdvancedFoldout.value = DoFoldout(title, m_AdvancedFoldout.value); + return m_AdvancedFoldout.value; + } + + public bool DoPlatformSettingsUI(GUIContent title) + { + m_PlatformSettingsFoldout.value = DoFoldout(title, m_PlatformSettingsFoldout.value); + return m_PlatformSettingsFoldout.value; + } + + public bool DoTextureUI(GUIContent title) + { + m_TextureFoldout.value = DoFoldout(title, m_TextureFoldout.value); + return m_TextureFoldout.value; + } + + class SavedBool + { + bool m_Value; + string m_Name; + bool m_Loaded; + + public SavedBool(string name, bool value) + { + m_Name = name; + m_Loaded = false; + m_Value = value; + } + + private void Load() + { + if (m_Loaded) + return; + + m_Loaded = true; + m_Value = EditorPrefs.GetBool(m_Name, m_Value); + } + + public bool value + { + get { Load(); return m_Value; } + set + { + Load(); + if (m_Value == value) + return; + m_Value = value; + EditorPrefs.SetBool(m_Name, value); + } + } + + public static implicit operator bool(SavedBool s) + { + return s.value; + } + } + } +} +#endif \ No newline at end of file diff --git a/Editor/PSDImproterEditorBefore_2021_3_9f1.cs.meta b/Editor/PSDImproterEditorBefore_2021_3_9f1.cs.meta new file mode 100644 index 0000000..3bfd0a0 --- /dev/null +++ b/Editor/PSDImproterEditorBefore_2021_3_9f1.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e0d9dd2800ae0492887701f7eee41be3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/PSDLayer.cs b/Editor/PSDLayer.cs index d5ca859..d792cd6 100644 --- a/Editor/PSDLayer.cs +++ b/Editor/PSDLayer.cs @@ -28,6 +28,8 @@ class PSDLayer : IPSDLayerMappingStrategyComparable [SerializeField] bool m_IsVisible; + [NonSerialized] + Vector2 m_LayerPosition; [NonSerialized] GameObject m_GameObject; @@ -55,6 +57,7 @@ public PSDLayer(NativeArray tex, int parent, bool group, string layerNa public int parentIndex { get { return m_ParentIndex; } private set { m_ParentIndex = value; } } public Vector2Int mosaicPosition { get { return m_MosaicPosition; } set { m_MosaicPosition = value; } } public GUID spriteID { get { return new GUID(m_SpriteID); } set { m_SpriteID = value.ToString(); } } + public Vector2 layerPosition { get => m_LayerPosition; set => m_LayerPosition = value; } public GameObject gameObject { get { return m_GameObject; } set { m_GameObject = value; } } public bool flatten @@ -70,8 +73,8 @@ public bool isImported } public NativeArray texture { get; set; } - public int width { get; private set; } - public int height { get; private set; } + public int width { get; set; } + public int height { get; set; } public void Dispose() { diff --git a/Editor/PSDPlugin/PDNWrapper/BitmapLayer.cs b/Editor/PSDPlugin/PDNWrapper/BitmapLayer.cs index cc3f423..ffff63e 100644 --- a/Editor/PSDPlugin/PDNWrapper/BitmapLayer.cs +++ b/Editor/PSDPlugin/PDNWrapper/BitmapLayer.cs @@ -6,44 +6,57 @@ internal static class Layer { public static BitmapLayer CreateBackgroundLayer(int w, int h) { - return new BitmapLayer(w, h); + return new BitmapLayer(new Rectangle(0, 0, w, h)); } } internal class BitmapLayer { - int width, height; + public int LayerID { get; set; } + public bool IsGroup {get; set; } + public BitmapLayer ParentLayer {get; set; } + public IEnumerable ChildLayer => m_ChildLayers; + public string Name { get; set; } + public byte Opacity { get; set; } + public bool Visible { get; set; } + public LayerBlendMode BlendMode { get; set; } + public Surface Surface { get; } + public Rectangle documentRect { get; private set; } + public Rectangle localRect { get; } - public Rectangle Bounds - { - get {return new Rectangle(0, 0, width, height); } - } + readonly List m_ChildLayers; public void Dispose() { Surface.Dispose(); - foreach (var layer in ChildLayer) + foreach (var layer in m_ChildLayers) layer.Dispose(); } - public BitmapLayer(int w, int h) + public BitmapLayer(Rectangle documentRect) { - Surface = new Surface(w, h); - width = w; - height = h; - ChildLayer = new List(); + localRect = new Rectangle(0, 0, documentRect.Width, documentRect.Height); + this.documentRect = documentRect; + + Surface = new Surface(localRect.Width, localRect.Height); + + m_ChildLayers = new List(); IsGroup = false; } - public int LayerID { get; set; } - public bool IsGroup {get; set; } - public BitmapLayer ParentLayer {get; set; } - public List ChildLayer { get; set; } - public string Name { get; set; } - public byte Opacity { get; set; } - public bool Visible { get; set; } - public LayerBlendMode BlendMode { get; set; } + public void AddChildLayer(BitmapLayer c) + { + m_ChildLayers.Add(c); + var bound = c.documentRect; + foreach (var child in ChildLayer) + { + bound.Y = bound.Y > child.documentRect.Y ? child.documentRect.Y : bound.Y; + bound.X = bound.X > child.documentRect.X ? child.documentRect.X : bound.X; + bound.Width = bound.Right < child.documentRect.Right ? child.documentRect.Right - bound.X : bound.Width; + bound.Height = bound.Bottom < child.documentRect.Bottom ? child.documentRect.Bottom - bound.Y : bound.Height; + } - public Surface Surface { get; set; } + documentRect = bound; + } } -} +} \ No newline at end of file diff --git a/Editor/PSDPlugin/PDNWrapper/PDNDecodeJob.cs b/Editor/PSDPlugin/PDNWrapper/PDNDecodeJob.cs index 292b358..c5cc2d6 100644 --- a/Editor/PSDPlugin/PDNWrapper/PDNDecodeJob.cs +++ b/Editor/PSDPlugin/PDNWrapper/PDNDecodeJob.cs @@ -1,6 +1,5 @@ -using System.Collections; -using System.Collections.Generic; using Unity.Jobs; +using Unity.Burst; using UnityEngine; using Unity.Collections; using System; @@ -8,17 +7,12 @@ namespace PaintDotNet.Data.PhotoshopFileType { - #region PDNDecodeJob internal struct PDNDecoderData { - // Inputs. public PDNWrapper.Rectangle Rect; - public PDNWrapper.Rectangle LayerRect; - public PDNWrapper.Rectangle ClippedRect; public int SurfaceWidth; - public int SurfaceHeight; public int SurfaceByteDepth; public DecodeType DecoderType; @@ -49,31 +43,30 @@ internal struct PDNDecoderData } + [BurstCompile] internal struct PDNDecoderJob : IJobParallelFor { - - public PDNDecoderData Data; + public PDNDecoderData data; public void Execute(int index) { - - int idx = Data.Rect.Top + index; + var idx = data.Rect.Top + index; { // Calculate index into ImageData source from row and column. - int idxSrcPixel = (idx - Data.LayerRect.Top) * Data.LayerRect.Width + (Data.Rect.Left - Data.LayerRect.Left); - int idxSrcBytes = idxSrcPixel * Data.SurfaceByteDepth; + var idxSrcPixel = (idx - data.Rect.Top) * data.Rect.Width + (data.Rect.Left - data.Rect.Left); + var idxSrcBytes = idxSrcPixel * data.SurfaceByteDepth; // Calculate pointers to destination Surface. - var idxDstStart = idx * Data.SurfaceWidth + Data.ClippedRect.Left; - var idxDstStops = idx * Data.SurfaceWidth + Data.ClippedRect.Right; + var idxDstStart = idx * data.SurfaceWidth + data.Rect.Left; + var idxDstStops = idx * data.SurfaceWidth + data.Rect.Right; // For 16-bit images, take the higher-order byte from the image data, which is now in little-endian order. - if (Data.SurfaceByteDepth == 2) + if (data.SurfaceByteDepth == 2) { idxSrcBytes++; } - switch (Data.DecoderType) + switch (data.DecoderType) { case DecodeType.RGB32: { @@ -116,24 +109,22 @@ public void Execute(int index) } break; } - } - } // Case 0: private void SetPDNRowRgb32(int dstStart, int dstStops, int idxSrc) { - NativeArray cR = Data.ColorChannel0.Reinterpret(1); - NativeArray cG = Data.ColorChannel1.Reinterpret(1); - NativeArray cB = Data.ColorChannel2.Reinterpret(1); - var c = Data.DecodedImage[dstStart]; + NativeArray cR = data.ColorChannel0.Reinterpret(1); + NativeArray cG = data.ColorChannel1.Reinterpret(1); + NativeArray cB = data.ColorChannel2.Reinterpret(1); + var c = data.DecodedImage[dstStart]; while (dstStart < dstStops) { c.r = ImageDecoderPdn.RGBByteFromHDRFloat(cR[idxSrc / 4]); c.g = ImageDecoderPdn.RGBByteFromHDRFloat(cG[idxSrc / 4]); c.b = ImageDecoderPdn.RGBByteFromHDRFloat(cB[idxSrc / 4]); - Data.DecodedImage[dstStart] = c; + data.DecodedImage[dstStart] = c; dstStart++; idxSrc += 4; @@ -143,15 +134,15 @@ private void SetPDNRowRgb32(int dstStart, int dstStops, int idxSrc) // Case 1: private void SetPDNRowGrayscale32(int dstStart, int dstStops, int idxSrc) { - NativeArray channel = Data.ColorChannel0.Reinterpret(1); - var c = Data.DecodedImage[dstStart]; + NativeArray channel = data.ColorChannel0.Reinterpret(1); + var c = data.DecodedImage[dstStart]; while (dstStart < dstStops) { byte rgbValue = ImageDecoderPdn.RGBByteFromHDRFloat(channel[idxSrc / 4]); c.r = rgbValue; c.g = rgbValue; c.b = rgbValue; - Data.DecodedImage[dstStart] = c; + data.DecodedImage[dstStart] = c; dstStart++; idxSrc += 4; @@ -161,16 +152,16 @@ private void SetPDNRowGrayscale32(int dstStart, int dstStops, int idxSrc) // Case 2: private void SetPDNRowRgb(int dstStart, int dstStops, int idxSrc) { - var c = Data.DecodedImage[dstStart]; + var c = data.DecodedImage[dstStart]; while (dstStart < dstStops) { - c.r = Data.ColorChannel0[idxSrc]; - c.g = Data.ColorChannel1[idxSrc]; - c.b = Data.ColorChannel2[idxSrc]; - Data.DecodedImage[dstStart] = c; + c.r = data.ColorChannel0[idxSrc]; + c.g = data.ColorChannel1[idxSrc]; + c.b = data.ColorChannel2[idxSrc]; + data.DecodedImage[dstStart] = c; dstStart++; - idxSrc += Data.SurfaceByteDepth; + idxSrc += data.SurfaceByteDepth; } } @@ -190,15 +181,15 @@ private void SetPDNRowRgb(int dstStart, int dstStops, int idxSrc) /////////////////////////////////////////////////////////////////////////////// private void SetPDNRowCmyk(int dstStart, int dstStops, int idxSrc) { - var c = Data.DecodedImage[dstStart]; + var c = data.DecodedImage[dstStart]; while (dstStart < dstStops) { // CMYK values are stored as complements, presumably to allow for some // measure of compatibility with RGB-only applications. - var C = 255 - Data.ColorChannel0[idxSrc]; - var M = 255 - Data.ColorChannel1[idxSrc]; - var Y = 255 - Data.ColorChannel2[idxSrc]; - var K = 255 - Data.ColorChannel3[idxSrc]; + var C = 255 - data.ColorChannel0[idxSrc]; + var M = 255 - data.ColorChannel1[idxSrc]; + var Y = 255 - data.ColorChannel2[idxSrc]; + var K = 255 - data.ColorChannel3[idxSrc]; int R = 255 - Math.Min(255, C * (255 - K) / 255 + K); int G = 255 - Math.Min(255, M * (255 - K) / 255 + K); @@ -207,77 +198,77 @@ private void SetPDNRowCmyk(int dstStart, int dstStops, int idxSrc) c.r = (byte)R; c.g = (byte)G; c.b = (byte)B; - Data.DecodedImage[dstStart] = c; + data.DecodedImage[dstStart] = c; dstStart++; - idxSrc += Data.SurfaceByteDepth; + idxSrc += data.SurfaceByteDepth; } } // Case 4: private void SetPDNRowBitmap(int dstStart, int dstStops, int idxSrc) { - var c = Data.DecodedImage[dstStart]; + var c = data.DecodedImage[dstStart]; while (dstStart < dstStops) { byte mask = (byte)(0x80 >> (idxSrc % 8)); - byte bwValue = (byte)(Data.ColorChannel0[idxSrc / 8] & mask); + byte bwValue = (byte)(data.ColorChannel0[idxSrc / 8] & mask); bwValue = (bwValue == 0) ? (byte)255 : (byte)0; c.r = bwValue; c.g = bwValue; c.b = bwValue; - Data.DecodedImage[dstStart] = c; + data.DecodedImage[dstStart] = c; dstStart++; - idxSrc += Data.SurfaceByteDepth; + idxSrc += data.SurfaceByteDepth; } } // Case 5: private void SetPDNRowGrayscale(int dstStart, int dstStops, int idxSrc) { - var c = Data.DecodedImage[dstStart]; + var c = data.DecodedImage[dstStart]; while (dstStart < dstStops) { - c.r = Data.ColorChannel0[idxSrc]; - c.g = Data.ColorChannel0[idxSrc]; - c.b = Data.ColorChannel0[idxSrc]; - Data.DecodedImage[dstStart] = c; + c.r = data.ColorChannel0[idxSrc]; + c.g = data.ColorChannel0[idxSrc]; + c.b = data.ColorChannel0[idxSrc]; + data.DecodedImage[dstStart] = c; dstStart++; - idxSrc += Data.SurfaceByteDepth; + idxSrc += data.SurfaceByteDepth; } } // Case 6: private void SetPDNRowIndexed(int dstStart, int dstStops, int idxSrc) { - var c = Data.DecodedImage[dstStart]; - int index = (int)Data.ColorChannel0[idxSrc]; + var c = data.DecodedImage[dstStart]; + int index = (int)data.ColorChannel0[idxSrc]; while (dstStart < dstStops) { - c.r = Data.ColorModeData[index]; - c.g = Data.ColorModeData[index + 256]; - c.b = Data.ColorModeData[index + 2 * 256]; - Data.DecodedImage[dstStart] = c; + c.r = data.ColorModeData[index]; + c.g = data.ColorModeData[index + 256]; + c.b = data.ColorModeData[index + 2 * 256]; + data.DecodedImage[dstStart] = c; dstStart++; - idxSrc += Data.SurfaceByteDepth; + idxSrc += data.SurfaceByteDepth; } } // Case 7: private void SetPDNRowLab(int dstStart, int dstStops, int idxSrc) { - var c = Data.DecodedImage[dstStart]; + var c = data.DecodedImage[dstStart]; while (dstStart < dstStops) { double exL, exA, exB; - exL = (double)Data.ColorChannel0[idxSrc]; - exA = (double)Data.ColorChannel1[idxSrc]; - exB = (double)Data.ColorChannel2[idxSrc]; + exL = (double)data.ColorChannel0[idxSrc]; + exA = (double)data.ColorChannel1[idxSrc]; + exB = (double)data.ColorChannel2[idxSrc]; int L = (int)(exL / 2.55); int a = (int)(exA - 127.5); @@ -360,10 +351,10 @@ private void SetPDNRowLab(int dstStart, int dstStops, int idxSrc) c.r = (byte)nRed; c.g = (byte)nGreen; c.b = (byte)nBlue; - Data.DecodedImage[dstStart] = c; + data.DecodedImage[dstStart] = c; dstStart++; - idxSrc += Data.SurfaceByteDepth; + idxSrc += data.SurfaceByteDepth; } } } @@ -374,12 +365,8 @@ private void SetPDNRowLab(int dstStart, int dstStops, int idxSrc) internal struct PDNAlphaMaskData { - // Inputs. public PDNWrapper.Rectangle Rect; - public PDNWrapper.Rectangle LayerRect; - public PDNWrapper.Rectangle ClippedRect; public int SurfaceWidth; - public int SurfaceHeight; public int SurfaceByteDepth; public int HasAlphaChannel; @@ -430,70 +417,70 @@ internal struct PDNAlphaMaskData public byte LayerMaskBackgroundColor; } + [BurstCompile] internal struct PDNAlphaMaskJob : IJob { - - public PDNAlphaMaskData Data; + public PDNAlphaMaskData data; public void Execute() { - - for (int idx = Data.Rect.Top; idx < Data.Rect.Bottom; idx++) + for (var idx = data.Rect.Top; idx < data.Rect.Bottom; idx++) { // Calculate index into ImageData source from row and column. - int idxSrcPixel = (idx - Data.LayerRect.Top) * Data.LayerRect.Width + (Data.Rect.Left - Data.LayerRect.Left); - int idxSrcBytes = idxSrcPixel * Data.SurfaceByteDepth; + var idxSrcPixel = (idx - data.Rect.Top) * data.Rect.Width + (data.Rect.Left - data.Rect.Left); + var idxSrcBytes = idxSrcPixel * data.SurfaceByteDepth; // Calculate pointers to destination Surface. - var idxDstStart = idx * Data.SurfaceWidth + Data.ClippedRect.Left; - var idxDstStops = idx * Data.SurfaceWidth + Data.ClippedRect.Right; + var idxDstStart = idx * data.SurfaceWidth + data.Rect.Left; + var idxDstStops = idx * data.SurfaceWidth + data.Rect.Right; // For 16-bit images, take the higher-order byte from the image data, which is now in little-endian order. - if (Data.SurfaceByteDepth == 2) + if (data.SurfaceByteDepth == 2) { idxSrcBytes++; } SetPDNAlphaRow(idxDstStart, idxDstStops, idxSrcBytes); - if (0 != Data.HasLayerAlphaMask) + + if (0 != data.HasLayerAlphaMask) { - GetMaskAlphaRow(idx, Data.LayerAlphaMask, Data.LayerAlphaMaskEmpty, Data.LayerMask, Data.LayerMaskInvertOnBlend, Data.LayerMaskBackgroundColor, Data.LayerMaskContextRect, Data.LayerMaskRect); + GetMaskAlphaRow(idx, data.LayerAlphaMask, data.LayerAlphaMaskEmpty, data.LayerMask, data.LayerMaskInvertOnBlend, data.LayerMaskBackgroundColor, data.LayerMaskContextRect, data.LayerMaskRect); } - if (0 != Data.HasUserAlphaMask) + if (0 != data.HasUserAlphaMask) { - GetMaskAlphaRow(idx, Data.UserAlphaMask, Data.UserAlphaMaskEmpty, Data.UserMask, Data.UserMaskInvertOnBlend, Data.UserMaskBackgroundColor, Data.UserMaskContextRect, Data.UserMaskRect); + GetMaskAlphaRow(idx, data.UserAlphaMask, data.UserAlphaMaskEmpty, data.UserMask, data.UserMaskInvertOnBlend, data.UserMaskBackgroundColor, data.UserMaskContextRect, data.UserMaskRect); } + ApplyPDNMask(idxDstStart, idxDstStops); - } } private void SetPDNAlphaRow(int dstStart, int dstStops, int idxSrc) { // Set alpha to fully-opaque if there is no alpha channel - if (0 == Data.HasAlphaChannel) + if (0 == data.HasAlphaChannel) { while (dstStart < dstStops) { - var c = Data.DecodedImage[dstStart]; + var c = data.DecodedImage[dstStart]; c.a = 255; - Data.DecodedImage[dstStart] = c; + data.DecodedImage[dstStart] = c; dstStart++; } } // Set the alpha channel data else { - NativeArray srcAlphaChannel = Data.AlphaChannel0.Reinterpret(1); + NativeArray srcAlphaChannel = data.AlphaChannel0.Reinterpret(1); { while (dstStart < dstStops) { - var c = Data.DecodedImage[dstStart]; - c.a = (Data.SurfaceByteDepth < 4) ? Data.AlphaChannel0[idxSrc] : ImageDecoderPdn.RGBByteFromHDRFloat(srcAlphaChannel[idxSrc / 4]); + var c = data.DecodedImage[dstStart]; + c.a = (data.SurfaceByteDepth < 4) ? data.AlphaChannel0[idxSrc] : ImageDecoderPdn.RGBByteFromHDRFloat(srcAlphaChannel[idxSrc / 4]); - Data.DecodedImage[dstStart] = c; + data.DecodedImage[dstStart] = c; dstStart++; - idxSrc += Data.SurfaceByteDepth; + idxSrc += data.SurfaceByteDepth; } } } @@ -502,22 +489,22 @@ private void SetPDNAlphaRow(int dstStart, int dstStops, int idxSrc) private void ApplyPDNMask(int dstStart, int dstStops) { // Do nothing if there are no masks - if (0 == Data.HasLayerAlphaMask && 0 == Data.HasUserAlphaMask) + if (0 == data.HasLayerAlphaMask && 0 == data.HasUserAlphaMask) { return; } // Apply one mask - else if (0 == Data.HasLayerAlphaMask || 0 == Data.HasUserAlphaMask) + else if (0 == data.HasLayerAlphaMask || 0 == data.HasUserAlphaMask) { - var maskAlpha = (0 == Data.HasLayerAlphaMask) ? Data.UserAlphaMask : Data.LayerAlphaMask; + var maskAlpha = (0 == data.HasLayerAlphaMask) ? data.UserAlphaMask : data.LayerAlphaMask; var maskStart = 0; { while (dstStart < dstStops) { - var c = Data.DecodedImage[dstStart]; - c.a = (byte)(Data.DecodedImage[dstStart].a * maskAlpha[maskStart] / 255); - Data.DecodedImage[dstStart] = c; + var c = data.DecodedImage[dstStart]; + c.a = (byte)(data.DecodedImage[dstStart].a * maskAlpha[maskStart] / 255); + data.DecodedImage[dstStart] = c; dstStart++; maskStart++; @@ -531,10 +518,10 @@ private void ApplyPDNMask(int dstStart, int dstStops) { while (dstStart < dstStops) { - var c = Data.DecodedImage[dstStart]; - var alphaFactor = (Data.LayerAlphaMask[maskStart]) * (Data.UserAlphaMask[maskStart]); - c.a = (byte)(Data.DecodedImage[dstStart].a * alphaFactor / 65025); - Data.DecodedImage[dstStart] = c; + var c = data.DecodedImage[dstStart]; + var alphaFactor = (data.LayerAlphaMask[maskStart]) * (data.UserAlphaMask[maskStart]); + c.a = (byte)(data.DecodedImage[dstStart].a * alphaFactor / 65025); + data.DecodedImage[dstStart] = c; dstStart++; maskStart++; @@ -572,7 +559,7 @@ private unsafe void GetMaskAlphaRow(int idxSrc, NativeArray alphaBuffer, N ////////////////////////////////////// // Transfer mask into the alpha array // Background color for areas not covered by the mask - byte backgroundColor = (0 != MaskInvertOnBlend) ? (byte)(255 - MaskBackgroundColor) : MaskBackgroundColor; + var backgroundColor = (0 != MaskInvertOnBlend) ? (byte)(255 - MaskBackgroundColor) : MaskBackgroundColor; { var alphaBufferPtr = NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(alphaBuffer); UnsafeUtility.MemSet(alphaBufferPtr, backgroundColor, alphaBuffer.Length); @@ -581,34 +568,34 @@ private unsafe void GetMaskAlphaRow(int idxSrc, NativeArray alphaBuffer, N if (alphaBufferEmpty[idxSrc] == 0) { // Get pointers to starting positions - int alphaColumn = MaskContextRect.X; + var alphaColumn = MaskContextRect.X; // It's possible that the layer's rect is larger than the clip and it's offset. // Since we only copy out the alpha based on the MaskContext size // The copy will start from where the MaskContextRect is - if(Data.LayerRect.X > 0) - alphaColumn = MaskContextRect.X - Data.LayerRect.X; + if(data.Rect.X > 0) + alphaColumn = MaskContextRect.X - data.Rect.X; var pAlpha = alphaColumn; var pAlphaEnd = pAlpha + MaskContextRect.Width; - int maskRow = idxSrc - MaskRect.Y; - int maskColumn = MaskContextRect.X - MaskRect.X; - int idxMaskPixel = (maskRow * MaskRect.Width) + maskColumn; - var pMask = idxMaskPixel * Data.SurfaceByteDepth; + var maskRow = idxSrc - MaskRect.Y; + var maskColumn = MaskContextRect.X - MaskRect.X; + var idxMaskPixel = (maskRow * MaskRect.Width) + maskColumn; + var pMask = idxMaskPixel * data.SurfaceByteDepth; // Take the high-order byte if values are 16-bit (little-endian) - if (Data.SurfaceByteDepth == 2) + if (data.SurfaceByteDepth == 2) { pMask++; } // Decode mask into the alpha array. - if (Data.SurfaceByteDepth == 4) + if (data.SurfaceByteDepth == 4) { DecodeMaskAlphaRow32(alphaBuffer, pAlpha, pAlphaEnd, maskChannel, pMask); } else { - DecodeMaskAlphaRow(alphaBuffer, pAlpha, pAlphaEnd, maskChannel, pMask, Data.SurfaceByteDepth); + DecodeMaskAlphaRow(alphaBuffer, pAlpha, pAlphaEnd, maskChannel, pMask, data.SurfaceByteDepth); } // Obsolete since Photoshop CS6, but retained for compatibility with older versions. Note that the background has already been inverted. diff --git a/Editor/PSDPlugin/PDNWrapper/Surface.cs b/Editor/PSDPlugin/PDNWrapper/Surface.cs index e8587c4..9026d9b 100644 --- a/Editor/PSDPlugin/PDNWrapper/Surface.cs +++ b/Editor/PSDPlugin/PDNWrapper/Surface.cs @@ -1,4 +1,5 @@ using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; using UnityEngine; namespace PDNWrapper diff --git a/Editor/PSDPlugin/PhotoShopFileType/ImageDecoderPdn.cs b/Editor/PSDPlugin/PhotoShopFileType/ImageDecoderPdn.cs index 9c3a0e2..f3f92a4 100644 --- a/Editor/PSDPlugin/PhotoShopFileType/ImageDecoderPdn.cs +++ b/Editor/PSDPlugin/PhotoShopFileType/ImageDecoderPdn.cs @@ -43,7 +43,7 @@ internal enum DecodeType internal static class ImageDecoderPdn { - private static double rgbExponent = 1 / 2.19921875; + const double rgbExponent = 1 / 2.19921875; private class DecodeContext { @@ -79,9 +79,7 @@ public DecodeContext(PhotoshopFile.Layer layer, Rectangle bounds) NativeArray.Copy(layer.AlphaChannel.ImageData, AlphaChannel, layer.AlphaChannel.ImageData.Length); ColorMode = layer.PsdFile.ColorMode; ColorModeData = new NativeArray(layer.PsdFile.ColorModeData, Allocator.TempJob); - - // Clip the layer to the specified bounds - Rectangle = Layer.Rect.IntersectWith(bounds); + Rectangle = bounds; if (layer.Masks != null) { @@ -89,7 +87,7 @@ public DecodeContext(PhotoshopFile.Layer layer, Rectangle bounds) UserMaskContext = GetMaskContext(layer.Masks.UserMask); } } - + internal void Cleanup() { AlphaChannel.Dispose(); @@ -191,7 +189,7 @@ private static DecodeDelegate GetDecodeDelegate32(PsdColorMode psdColorMode, ref public static JobHandle DecodeImage(BitmapLayer pdnLayer, PhotoshopFile.Layer psdLayer, JobHandle inputDeps) { UnityEngine.Profiling.Profiler.BeginSample("DecodeImage"); - var decodeContext = new DecodeContext(psdLayer, pdnLayer.Bounds); + var decodeContext = new DecodeContext(psdLayer, pdnLayer.localRect); DecodeDelegate decoder = null; DecodeType decoderType = 0; @@ -209,50 +207,45 @@ public static JobHandle DecodeImage(BitmapLayer pdnLayer, PhotoshopFile.Layer ps /// Decode image from Photoshop's channel-separated formats to BGRA, /// using the specified decode delegate on each row. /// - private static JobHandle DecodeImage(BitmapLayer pdnLayer, DecodeContext decodeContext, DecodeType decoderType, JobHandle inputDeps) + static JobHandle DecodeImage(BitmapLayer pdnLayer, DecodeContext decodeContext, DecodeType decoderType, JobHandle inputDeps) { - - var psdLayer = decodeContext.Layer; var surface = pdnLayer.Surface; var rect = decodeContext.Rectangle; // Convert rows from the Photoshop representation, writing the // resulting ARGB values to to the Paint.NET Surface. - int jobCount = Unity.Jobs.LowLevel.Unsafe.JobsUtility.JobWorkerMaximumCount; - int execCount = (rect.Bottom - rect.Top); - int sliceCount = execCount / jobCount; - PDNDecoderJob decoderJob = new PDNDecoderJob(); - - decoderJob.Data.Rect = rect; - decoderJob.Data.LayerRect = psdLayer.Rect; - decoderJob.Data.ClippedRect = rect; - decoderJob.Data.SurfaceWidth = surface.width; - decoderJob.Data.SurfaceHeight = surface.height; - decoderJob.Data.SurfaceByteDepth = decodeContext.ByteDepth; - decoderJob.Data.DecoderType = decoderType; - - decoderJob.Data.ColorChannel0 = decodeContext.Channels[0].ImageData; - decoderJob.Data.ColorChannel1 = decodeContext.Channels.Length > 1 ? decodeContext.Channels[1].ImageData : decodeContext.Channels[0].ImageData; - decoderJob.Data.ColorChannel2 = decodeContext.Channels.Length > 2 ? decodeContext.Channels[2].ImageData : decodeContext.Channels[0].ImageData; - decoderJob.Data.ColorChannel3 = decodeContext.Channels.Length > 3 ? decodeContext.Channels[3].ImageData : decodeContext.Channels[0].ImageData; - decoderJob.Data.ColorModeData = decodeContext.ColorModeData; - decoderJob.Data.DecodedImage = surface.color; + var jobCount = Unity.Jobs.LowLevel.Unsafe.JobsUtility.JobWorkerMaximumCount; + var execCount = (rect.Bottom - rect.Top); + var sliceCount = execCount / jobCount; + var decoderJob = new PDNDecoderJob(); + + decoderJob.data.Rect = rect; + decoderJob.data.SurfaceWidth = surface.width; + decoderJob.data.SurfaceByteDepth = decodeContext.ByteDepth; + decoderJob.data.DecoderType = decoderType; + + decoderJob.data.ColorChannel0 = decodeContext.Channels[0].ImageData; + decoderJob.data.ColorChannel1 = decodeContext.Channels.Length > 1 ? decodeContext.Channels[1].ImageData : decodeContext.Channels[0].ImageData; + decoderJob.data.ColorChannel2 = decodeContext.Channels.Length > 2 ? decodeContext.Channels[2].ImageData : decodeContext.Channels[0].ImageData; + decoderJob.data.ColorChannel3 = decodeContext.Channels.Length > 3 ? decodeContext.Channels[3].ImageData : decodeContext.Channels[0].ImageData; + decoderJob.data.ColorModeData = decodeContext.ColorModeData; + decoderJob.data.DecodedImage = surface.color; // Schedule the job, returns the JobHandle which can be waited upon later on - JobHandle jobHandle = decoderJob.Schedule(execCount, sliceCount, inputDeps); + var jobHandle = decoderJob.Schedule(execCount, sliceCount, inputDeps); // Mask and Alpha. - int userMaskContextSize = decodeContext.UserMaskContext != null ? decodeContext.Rectangle.Width : 1; - int layerMaskContextSize = decodeContext.LayerMaskContext != null ? decodeContext.Rectangle.Width : 1; + var userMaskContextSize = decodeContext.UserMaskContext != null ? decodeContext.Rectangle.Width : 1; + var layerMaskContextSize = decodeContext.LayerMaskContext != null ? decodeContext.Rectangle.Width : 1; var userAlphaMask = new NativeArray(userMaskContextSize, Allocator.TempJob); var layerAlphaMask = new NativeArray(layerMaskContextSize, Allocator.TempJob); var userAlphaMaskEmpty = new NativeArray(rect.Bottom, Allocator.TempJob); var layerAlphaMaskEmpty = new NativeArray(rect.Bottom, Allocator.TempJob); - PDNAlphaMaskJob alphaMaskJob = new PDNAlphaMaskJob(); + var alphaMaskJob = new PDNAlphaMaskJob(); - for (int y = rect.Top; y < rect.Bottom; ++y) + for (var y = rect.Top; y < rect.Bottom; ++y) { if (decodeContext.UserMaskContext != null) userAlphaMaskEmpty[y] = decodeContext.UserMaskContext.IsRowEmpty(y) ? (byte)1 : (byte)0; @@ -260,37 +253,33 @@ private static JobHandle DecodeImage(BitmapLayer pdnLayer, DecodeContext decodeC layerAlphaMaskEmpty[y] = decodeContext.LayerMaskContext.IsRowEmpty(y) ? (byte)1 : (byte)0; } - alphaMaskJob.Data.Rect = rect; - alphaMaskJob.Data.LayerRect = psdLayer.Rect; - alphaMaskJob.Data.ClippedRect = rect; - alphaMaskJob.Data.SurfaceWidth = surface.width; - alphaMaskJob.Data.SurfaceHeight = surface.height; - alphaMaskJob.Data.SurfaceByteDepth = decodeContext.ByteDepth; - alphaMaskJob.Data.HasAlphaChannel = decodeContext.HasAlphaChannel; - - alphaMaskJob.Data.HasUserAlphaMask = decodeContext.UserMaskContext != null ? 1 : 0; - alphaMaskJob.Data.UserMaskInvertOnBlend = decodeContext.UserMaskContext != null ? (decodeContext.UserMaskContext.Mask.InvertOnBlend ? 1 : 0) : 0; - alphaMaskJob.Data.UserMaskRect = decodeContext.UserMaskContext != null ? decodeContext.UserMaskContext.Mask.Rect : rect; - alphaMaskJob.Data.UserMaskContextRect = decodeContext.UserMaskContext != null ? decodeContext.UserMaskContext.Rectangle : rect; - alphaMaskJob.Data.HasLayerAlphaMask = decodeContext.LayerMaskContext != null ? 1 : 0; - alphaMaskJob.Data.LayerMaskInvertOnBlend = decodeContext.LayerMaskContext != null ? (decodeContext.LayerMaskContext.Mask.InvertOnBlend ? 1 : 0) : 0; - alphaMaskJob.Data.LayerMaskRect = decodeContext.LayerMaskContext != null ? decodeContext.LayerMaskContext.Mask.Rect : rect; - alphaMaskJob.Data.LayerMaskContextRect = decodeContext.LayerMaskContext != null ? decodeContext.LayerMaskContext.Rectangle : rect; - - alphaMaskJob.Data.AlphaChannel0 = decodeContext.AlphaChannel; - alphaMaskJob.Data.UserMask = decodeContext.UserMaskContext != null ? decodeContext.UserMaskContext.Mask.ImageData : decodeContext.AlphaChannel; - alphaMaskJob.Data.UserAlphaMask = userAlphaMask; - alphaMaskJob.Data.UserAlphaMaskEmpty = userAlphaMaskEmpty; - alphaMaskJob.Data.LayerMask = decodeContext.LayerMaskContext != null ? decodeContext.LayerMaskContext.Mask.ImageData : decodeContext.AlphaChannel; - alphaMaskJob.Data.LayerAlphaMask = layerAlphaMask; - alphaMaskJob.Data.LayerAlphaMaskEmpty = layerAlphaMaskEmpty; - alphaMaskJob.Data.DecodedImage = surface.color; - alphaMaskJob.Data.UserMaskBackgroundColor = decodeContext.UserMaskContext != null ? decodeContext.UserMaskContext.Mask.BackgroundColor : (byte)0; - alphaMaskJob.Data.LayerMaskBackgroundColor = decodeContext.LayerMaskContext != null ? decodeContext.LayerMaskContext.Mask.BackgroundColor : (byte)0; + alphaMaskJob.data.Rect = rect; + alphaMaskJob.data.SurfaceWidth = surface.width; + alphaMaskJob.data.SurfaceByteDepth = decodeContext.ByteDepth; + alphaMaskJob.data.HasAlphaChannel = decodeContext.HasAlphaChannel; + + alphaMaskJob.data.HasUserAlphaMask = decodeContext.UserMaskContext != null ? 1 : 0; + alphaMaskJob.data.UserMaskInvertOnBlend = decodeContext.UserMaskContext != null ? (decodeContext.UserMaskContext.Mask.InvertOnBlend ? 1 : 0) : 0; + alphaMaskJob.data.UserMaskRect = decodeContext.UserMaskContext != null ? decodeContext.UserMaskContext.Mask.Rect : rect; + alphaMaskJob.data.UserMaskContextRect = decodeContext.UserMaskContext != null ? decodeContext.UserMaskContext.Rectangle : rect; + alphaMaskJob.data.HasLayerAlphaMask = decodeContext.LayerMaskContext != null ? 1 : 0; + alphaMaskJob.data.LayerMaskInvertOnBlend = decodeContext.LayerMaskContext != null ? (decodeContext.LayerMaskContext.Mask.InvertOnBlend ? 1 : 0) : 0; + alphaMaskJob.data.LayerMaskRect = decodeContext.LayerMaskContext != null ? decodeContext.LayerMaskContext.Mask.Rect : rect; + alphaMaskJob.data.LayerMaskContextRect = decodeContext.LayerMaskContext != null ? decodeContext.LayerMaskContext.Rectangle : rect; + + alphaMaskJob.data.AlphaChannel0 = decodeContext.AlphaChannel; + alphaMaskJob.data.UserMask = decodeContext.UserMaskContext != null ? decodeContext.UserMaskContext.Mask.ImageData : decodeContext.AlphaChannel; + alphaMaskJob.data.UserAlphaMask = userAlphaMask; + alphaMaskJob.data.UserAlphaMaskEmpty = userAlphaMaskEmpty; + alphaMaskJob.data.LayerMask = decodeContext.LayerMaskContext != null ? decodeContext.LayerMaskContext.Mask.ImageData : decodeContext.AlphaChannel; + alphaMaskJob.data.LayerAlphaMask = layerAlphaMask; + alphaMaskJob.data.LayerAlphaMaskEmpty = layerAlphaMaskEmpty; + alphaMaskJob.data.DecodedImage = surface.color; + alphaMaskJob.data.UserMaskBackgroundColor = decodeContext.UserMaskContext != null ? decodeContext.UserMaskContext.Mask.BackgroundColor : (byte)0; + alphaMaskJob.data.LayerMaskBackgroundColor = decodeContext.LayerMaskContext != null ? decodeContext.LayerMaskContext.Mask.BackgroundColor : (byte)0; jobHandle = alphaMaskJob.Schedule(jobHandle); return jobHandle; - } /////////////////////////////////////////////////////////////////////////// @@ -303,7 +292,7 @@ private static JobHandle DecodeImage(BitmapLayer pdnLayer, DecodeContext decodeC public static void DecodeImage(BitmapLayer pdnLayer, PhotoshopFile.Layer psdLayer) { UnityEngine.Profiling.Profiler.BeginSample("DecodeImage"); - var decodeContext = new DecodeContext(psdLayer, pdnLayer.Bounds); + var decodeContext = new DecodeContext(psdLayer, pdnLayer.localRect); DecodeDelegate decoder = null; DecodeType decoderType = 0; diff --git a/Editor/PSDPlugin/PhotoShopFileType/PsdLoad.cs b/Editor/PSDPlugin/PhotoShopFileType/PsdLoad.cs index fa2fb4e..2f9ed8e 100644 --- a/Editor/PSDPlugin/PhotoShopFileType/PsdLoad.cs +++ b/Editor/PSDPlugin/PhotoShopFileType/PsdLoad.cs @@ -91,7 +91,7 @@ public static Document Load(System.IO.Stream input) jobHandle = l.DecodeToPdnLayer(jobHandle, out b); b.ParentLayer = parent; if (parent != null) - parent.ChildLayer.Add(b); + parent.AddChildLayer(b); else document.Layers.Add(b); @@ -107,10 +107,9 @@ public static Document Load(System.IO.Stream input) internal static JobHandle DecodeToPdnLayer(this PhotoshopFile.Layer psdLayer, JobHandle inputDeps, out BitmapLayer pdnLayer) { - var psdFile = psdLayer.PsdFile; psdLayer.CreateMissingChannels(); - - pdnLayer = new BitmapLayer(psdFile.ColumnCount, psdFile.RowCount); + + pdnLayer = new BitmapLayer(psdLayer.Rect); pdnLayer.Name = psdLayer.Name; pdnLayer.Opacity = psdLayer.Opacity; pdnLayer.Visible = psdLayer.Visible; @@ -303,4 +302,4 @@ be enough memory to import */ } } -} +} \ No newline at end of file diff --git a/Editor/PSDPlugin/PsdPlugin.asmdef b/Editor/PSDPlugin/PsdPlugin.asmdef index 7ae1f06..14d52d7 100644 --- a/Editor/PSDPlugin/PsdPlugin.asmdef +++ b/Editor/PSDPlugin/PsdPlugin.asmdef @@ -1,5 +1,8 @@ { "name": "PsdPlugin", + "references": [ + "Unity.Burst" + ], "optionalUnityReferences": [], "includePlatforms": [ "Editor" diff --git a/Editor/SpriteData.cs b/Editor/SpriteData.cs index 86cf51d..ee032f2 100644 --- a/Editor/SpriteData.cs +++ b/Editor/SpriteData.cs @@ -19,6 +19,7 @@ internal class SpriteMetaData : SpriteRect public float tessellationDetail; public int parentGroupIndex = -1; public Vector2Int uvTransform = Vector2Int.zero; + public Vector2 spritePosition; public SpriteMetaData() {} diff --git a/Editor/Tasks/ExtractLayerTask.cs b/Editor/Tasks/ExtractLayerTask.cs index 00dfebd..904f3ba 100644 --- a/Editor/Tasks/ExtractLayerTask.cs +++ b/Editor/Tasks/ExtractLayerTask.cs @@ -1,173 +1,249 @@ using System; using System.Collections.Generic; -using System.Linq; -using PDNWrapper; using UnityEngine; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Jobs; +using Unity.Mathematics; +using Unity.Burst; namespace UnityEditor.U2D.PSD { - class ExtractLayerTask + internal static class ExtractLayerTask { - struct LayerExtractData + struct LayerGroupData { - public int start; - public int end; - public int width; - public int height; + public int startIndex { get; set; } + public int endIndex { get; set; } + + /// + /// The layer's bounding box in document space. + /// + public int4 documentRect { get; set; } + + public int width => documentRect.z; + public int height => documentRect.w; } + [BurstCompile] struct ConvertBufferJob : IJobParallelFor { - - [ReadOnly] - [DeallocateOnJobCompletion] - public NativeArray original; - [ReadOnly] - [DeallocateOnJobCompletion] - public NativeArray flattenIndex; - [ReadOnly] - [DeallocateOnJobCompletion] - public NativeArray width; - [ReadOnly] - [DeallocateOnJobCompletion] - public NativeArray height; + [ReadOnly, DeallocateOnJobCompletion] + public NativeArray inputTextureBufferSizes; + [ReadOnly, DeallocateOnJobCompletion] + public NativeArray inputTextures; + [ReadOnly, DeallocateOnJobCompletion] + public NativeArray inputLayerRects; + [ReadOnly, DeallocateOnJobCompletion] + public NativeArray layerGroupDataData; + [ReadOnly, DeallocateOnJobCompletion] + public NativeArray outputTextureSizes; [DeallocateOnJobCompletion] - public NativeArray output; - public unsafe void Execute(int index) + public NativeArray outputTextures; + public unsafe void Execute(int index) { - Color32* outputColor = (Color32*)output[index]; - for (int i = flattenIndex[index].start; i <= flattenIndex[index].end; ++i) + var outputColor = (Color32*)outputTextures[index]; + var groupStartIndex = layerGroupDataData[index].startIndex; + var groupEndIndex = layerGroupDataData[index].endIndex; + var groupRect = layerGroupDataData[index].documentRect; + + var outWidth = outputTextureSizes[index].x; + var outHeight = outputTextureSizes[index].y; + + for (var i = groupEndIndex; i >= groupStartIndex; --i) { - if (original[i] == IntPtr.Zero) + if (inputTextures[i] == IntPtr.Zero) continue; - Color32* originalColor = (Color32*)original[i]; - int bufferWidth = width[i]; - int bufferHeight = height[i]; - for (int h = 0; h < bufferHeight; ++h) + + var inputColor = (Color32*)inputTextures[i]; + var inWidth = inputLayerRects[i].z; + var inHeight = inputLayerRects[i].w; + + var layerToGroupSpace = new int2(inputLayerRects[i].x - groupRect.x, inputLayerRects[i].y - groupRect.y); + // Flip Y position + layerToGroupSpace.y = outHeight - layerToGroupSpace.y - inHeight; + + for (var y = 0; y < inHeight; ++y) { - int originalYOffset = h * bufferWidth; - int outputYOffset = (bufferHeight - h - 1) * bufferWidth; - for (int w = 0; w < bufferWidth; ++w) + var inY = y * inWidth; + var outY = (outHeight - 1 - y - layerToGroupSpace.y) * outWidth; + + for (var x = 0; x < inWidth; ++x) { - var outColor = outputColor[w + outputYOffset]; - var inColor = originalColor[w + originalYOffset]; - float alpha = outColor.a / 255.0f; - outColor.r = (byte)(alpha * (float)(outColor.r) + (float)((1.0f - alpha) * (float)inColor.r)); - outColor.g = (byte)(alpha * (float)(outColor.g) + (float)((1.0f - alpha) * (float)inColor.g)); - outColor.b = (byte)(alpha * (float)(outColor.b) + (float)((1.0f - alpha) * (float)inColor.b)); - outColor.a = (byte)(alpha * (float)(outColor.a) + (float)((1.0f - alpha) * (float)inColor.a)); - outputColor[w + outputYOffset] = outColor; + var inX = inY + x; + var outX = outY + x + layerToGroupSpace.x; + + if (inX >= inputTextureBufferSizes[i]) + break; + if (outX >= (outWidth * outHeight)) + break; + + Color inColor = inputColor[inX]; + Color prevOutColor = outputColor[outX]; + var outColor = new Color(); + + var destAlpha = prevOutColor.a * (1 - inColor.a); + outColor.a = inColor.a + prevOutColor.a * (1 - inColor.a); + var premultiplyAlpha = 1 / outColor.a; + outColor.r = (inColor.r * inColor.a + prevOutColor.r * destAlpha) * premultiplyAlpha; + outColor.g = (inColor.g * inColor.a + prevOutColor.g * destAlpha) * premultiplyAlpha; + outColor.b = (inColor.b * inColor.a + prevOutColor.b * destAlpha) * premultiplyAlpha; + + outputColor[outX] = outColor; } } } } } - public static unsafe void Execute(List extractedLayer, List layers, bool importHiddenLayer, FlattenLayerData[] previousFlattenLayer, IPSDLayerMappingStrategy mappingStrategy) + public static unsafe void Execute(in PSDExtractLayerData[] inputLayers, out List outputLayers, bool importHiddenLayer, Vector2Int documentSize) { + outputLayers = new List(); UnityEngine.Profiling.Profiler.BeginSample("ExtractLayer_PrepareJob"); - List layerToExtract = new List(); - ExtractLayer(extractedLayer, layers, importHiddenLayer, false, layerToExtract, previousFlattenLayer, mappingStrategy, true); - if (layerToExtract.Count == 0) + + var layerGroupData = new List(); + ExtractLayer(in inputLayers, ref outputLayers, ref layerGroupData, importHiddenLayer, false, true, documentSize); + + if (layerGroupData.Count == 0) { - foreach (var l in extractedLayer) - l.texture = default; + foreach (var layer in outputLayers) + layer.texture = default; return; } - + var job = new ConvertBufferJob(); - job.original = new NativeArray(extractedLayer.Count, Allocator.TempJob); - job.output = new NativeArray(layerToExtract.Count, Allocator.TempJob); - job.width = new NativeArray(extractedLayer.Count, Allocator.TempJob); - job.height = new NativeArray(extractedLayer.Count, Allocator.TempJob); + job.inputTextureBufferSizes = new NativeArray(outputLayers.Count, Allocator.TempJob); + job.inputTextures = new NativeArray(outputLayers.Count, Allocator.TempJob); + job.inputLayerRects = new NativeArray(outputLayers.Count, Allocator.TempJob); + job.outputTextureSizes = new NativeArray(layerGroupData.Count, Allocator.TempJob); + job.outputTextures = new NativeArray(layerGroupData.Count, Allocator.TempJob); - for (int i = 0, extractLayerIndex = 0; i < extractedLayer.Count; ++i) + for (int i = 0, outputLayerIndex = 0; i < outputLayers.Count; ++i) { - var el = extractedLayer[i]; - job.original[i] = el.texture.IsCreated ? new IntPtr(el.texture.GetUnsafePtr()) : IntPtr.Zero; - if (extractLayerIndex < layerToExtract.Count && layerToExtract[extractLayerIndex].start == i) + var outputLayer = outputLayers[i]; + + job.inputTextures[i] = outputLayer.texture.IsCreated ? new IntPtr(outputLayer.texture.GetUnsafePtr()) : IntPtr.Zero; + + if (outputLayerIndex < layerGroupData.Count && layerGroupData[outputLayerIndex].startIndex == i) { - el.texture = new NativeArray(layerToExtract[extractLayerIndex].width * layerToExtract[extractLayerIndex].height, Allocator.Persistent); - job.output[extractLayerIndex] = el.texture.IsCreated ? new IntPtr(el.texture.GetUnsafePtr()) : IntPtr.Zero; - ++extractLayerIndex; + var inputLayer = layerGroupData[outputLayerIndex]; + + outputLayer.texture = new NativeArray(inputLayer.width * inputLayer.height, Allocator.Persistent); + job.outputTextureSizes[outputLayerIndex] = new int2(inputLayer.width, inputLayer.height); + job.outputTextures[outputLayerIndex] = outputLayer.texture.IsCreated ? new IntPtr(outputLayer.texture.GetUnsafePtr()) : IntPtr.Zero; + job.inputTextureBufferSizes[i] = outputLayer.texture.IsCreated ? outputLayer.texture.Length : -1; + ++outputLayerIndex; } else { - el.texture = default; + job.inputTextureBufferSizes[i] = outputLayer.texture.IsCreated ? outputLayer.texture.Length : -1; + outputLayer.texture = default; } - job.width[i] = el.width; - job.height[i] = el.height; + job.inputLayerRects[i] = new int4((int)outputLayer.layerPosition.x, (int)outputLayer.layerPosition.y, outputLayer.width, outputLayer.height); } - job.flattenIndex = new NativeArray(layerToExtract.ToArray(), Allocator.TempJob); + job.layerGroupDataData = new NativeArray(layerGroupData.ToArray(), Allocator.TempJob); - var jobsPerThread = layerToExtract.Count / (SystemInfo.processorCount == 0 ? 8 : SystemInfo.processorCount); + var jobsPerThread = layerGroupData.Count / (SystemInfo.processorCount == 0 ? 8 : SystemInfo.processorCount); jobsPerThread = Mathf.Max(jobsPerThread, 1); - var handle = job.Schedule(layerToExtract.Count, jobsPerThread); + var handle = job.Schedule(layerGroupData.Count, jobsPerThread); UnityEngine.Profiling.Profiler.EndSample(); handle.Complete(); } - static (int width, int height) ExtractLayer(List extractedLayer, List layers, bool importHiddenLayer, bool flatten, List layerExtract, FlattenLayerData[] previousFlatten, IPSDLayerMappingStrategy mappingStrategy, bool parentGroupVisible) + static Rect ExtractLayer(in PSDExtractLayerData[] inputLayers, ref List outputLayers, ref List layerGroupData, bool importHiddenLayer, bool flatten, bool parentGroupVisible, Vector2Int documentSize) { - // parent is the previous element in extracedLayer - int parentGroupIndex = extractedLayer.Count - 1; - int maxWidth = 0, maxHeight = 0; - int width = 0, height = 0; - foreach (var l in layers) + // parent is the previous element in extractedLayer + var parentGroupIndex = outputLayers.Count - 1; + var layerBoundingBox = default(Rect); + + foreach (var inputLayer in inputLayers) { - bool layerVisible = l.Visible && parentGroupVisible; - if (l.IsGroup) + var bitmapLayer = inputLayer.bitmapLayer; + var importSettings = inputLayer.importSetting; + var layerVisible = bitmapLayer.Visible && parentGroupVisible; + + var layerRect = new Rect(float.MaxValue, float.MaxValue, 0f, 0f); + if (inputLayer.bitmapLayer.IsGroup) { - var layer = new PSDLayer(l.Surface.color, parentGroupIndex, l.IsGroup, l.Name, 0, 0, l.LayerID, l.Visible); - layer.flatten = previousFlatten == null ? false : previousFlatten.FirstOrDefault(x => mappingStrategy.Compare(x, l)) != null; - layer.isImported = (importHiddenLayer || layerVisible) && !flatten && layer.flatten; - int startIndex = extractedLayer.Count; - extractedLayer.Add(layer); - (width, height) = ExtractLayer(extractedLayer, l.ChildLayer, importHiddenLayer, flatten || layer.flatten, layerExtract, previousFlatten, mappingStrategy, layerVisible); - int endIndex = extractedLayer.Count - 1; + var outputLayer = new PSDLayer(bitmapLayer.Surface.color, parentGroupIndex, bitmapLayer.IsGroup, bitmapLayer.Name, 0, 0, bitmapLayer.LayerID, bitmapLayer.Visible) + { + layerPosition = Vector2.zero, + spriteID = inputLayer.importSetting.spriteId, + flatten = inputLayer.importSetting.flatten + }; + outputLayer.isImported = (importHiddenLayer || layerVisible) && !flatten && outputLayer.flatten; + + var startIndex = outputLayers.Count; + outputLayers.Add(outputLayer); + layerRect = ExtractLayer(in inputLayer.children, ref outputLayers, ref layerGroupData, importHiddenLayer, flatten || outputLayer.flatten, layerVisible, documentSize); + var endIndex = outputLayers.Count - 1; + // If this group is to be flatten and there are flatten layers - if (flatten == false && layer.flatten && startIndex < endIndex) + if (flatten == false && outputLayer.flatten && startIndex < endIndex) { - layerExtract.Add(new LayerExtractData() + layerGroupData.Add(new LayerGroupData() { - start = startIndex, - end = endIndex, - width = width, - height = height - }); + startIndex = startIndex, + endIndex = endIndex, + documentRect = new int4((int)layerRect.x, (int)layerRect.y, (int)layerRect.width, (int)layerRect.height) + }); + + outputLayer.texture = default; + outputLayer.layerPosition = new Vector2(layerRect.x, layerRect.y); + outputLayer.width = (int)layerRect.width; + outputLayer.height = (int)layerRect.height; } } else { - var surface = importHiddenLayer || l.Visible ? l.Surface.color : default; - var layer = new PSDLayer(surface, parentGroupIndex, l.IsGroup, l.Name, l.Surface.width, l.Surface.height, l.LayerID,l.Visible); - layer.isImported = (importHiddenLayer || layerVisible) && !flatten; - extractedLayer.Add(layer); - if (layer.isImported) + var layerRectDocSpace = bitmapLayer.documentRect; + // From Photoshop "space" into Unity "space" + layerRectDocSpace.Y = (documentSize.y - layerRectDocSpace.Y) - layerRectDocSpace.Height; + + var surface = (importHiddenLayer || bitmapLayer.Visible) ? bitmapLayer.Surface.color : default; + var outputLayer = new PSDLayer(surface, parentGroupIndex, bitmapLayer.IsGroup, bitmapLayer.Name, bitmapLayer.Surface.width, bitmapLayer.Surface.height, bitmapLayer.LayerID,bitmapLayer.Visible) + { + spriteID = importSettings.spriteId, + layerPosition = new Vector2(layerRectDocSpace.X, layerRectDocSpace.Y) + }; + outputLayer.isImported = (importHiddenLayer || layerVisible) && !flatten; + outputLayers.Add(outputLayer); + if (outputLayer.isImported) { - layerExtract.Add(new LayerExtractData() + layerGroupData.Add(new LayerGroupData() { - start = extractedLayer.Count-1, - end = extractedLayer.Count-1, - width = l.Surface.width, - height = l.Surface.height, + startIndex = outputLayers.Count - 1, + endIndex = outputLayers.Count - 1, + documentRect = new int4(layerRectDocSpace.X, layerRectDocSpace.Y, layerRectDocSpace.Width, layerRectDocSpace.Height) }); } - width = l.Surface.width; - height = l.Surface.height; + layerRect.x = layerRectDocSpace.X; + layerRect.y = layerRectDocSpace.Y; + layerRect.width = bitmapLayer.Surface.width; + layerRect.height = bitmapLayer.Surface.height; } - if (maxWidth < width) - maxWidth = width; - if (maxHeight < height) - maxHeight = height; + + if (layerBoundingBox == default) + layerBoundingBox = layerRect; + else + { + if (layerBoundingBox.xMin > layerRect.xMin) + layerBoundingBox.xMin = layerRect.xMin; + if (layerBoundingBox.yMin > layerRect.yMin) + layerBoundingBox.yMin = layerRect.yMin; + if (layerBoundingBox.xMax < layerRect.xMax) + layerBoundingBox.xMax = layerRect.xMax; + if (layerBoundingBox.yMax < layerRect.yMax) + layerBoundingBox.yMax = layerRect.yMax; + } + + layerBoundingBox.width = Mathf.Min(layerBoundingBox.width, documentSize.x); + layerBoundingBox.height = Mathf.Min(layerBoundingBox.height, documentSize.y); } - return (maxWidth, maxHeight); + return layerBoundingBox; } } -} +} \ No newline at end of file diff --git a/Editor/Tasks/FlattenImageTask.cs b/Editor/Tasks/FlattenImageTask.cs index 51c3b3e..fff049a 100644 --- a/Editor/Tasks/FlattenImageTask.cs +++ b/Editor/Tasks/FlattenImageTask.cs @@ -1,132 +1,171 @@ using System; using System.Collections.Generic; -using PDNWrapper; +using Unity.Burst; using UnityEngine; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Jobs; +using Unity.Mathematics; namespace UnityEditor.U2D.PSD { static class FlattenImageTask { - static unsafe public void Execute(List layer, bool importHiddenLayer, int width, int height, NativeArray output) + struct LayerData + { + public IntPtr layerBuffer; + public int4 layerRect; + } + + public static unsafe void Execute(in PSDExtractLayerData[] layer, ref NativeArray output, bool importHiddenLayer, Vector2Int documentSize) { UnityEngine.Profiling.Profiler.BeginSample("FlattenImage"); - List buffers = new List(); - for (int i = layer.Count - 1; i >= 0; --i) + + var layerData = new List(); + for (var i = layer.Length - 1; i >= 0; --i) { - GetBuffersToMergeFromLayer(layer[i], importHiddenLayer, buffers); + GetLayerDataToMerge(in layer[i], ref layerData, importHiddenLayer); } - if (buffers.Count == 0) + if (layerData.Count == 0) return; - var layersPerJob = buffers.Count / (SystemInfo.processorCount == 0 ? 8 : SystemInfo.processorCount); + var layersPerJob = layerData.Count / (SystemInfo.processorCount == 0 ? 8 : SystemInfo.processorCount); layersPerJob = Mathf.Max(layersPerJob, 1); var job = new FlattenImageInternalJob(); var combineJob = new FlattenImageInternalJob(); - job.buffers = new NativeArray(buffers.ToArray(), Allocator.TempJob); - for (int i = 0; i < buffers.Count; ++i) - job.buffers[i] = buffers[i]; - - combineJob.width = job.width = width; - combineJob.height = job.height = height; + job.inputTextures = new NativeArray(layerData.Count, Allocator.TempJob); + job.inputTextureRects = new NativeArray(layerData.Count, Allocator.TempJob); + + for (var i = 0; i < layerData.Count; ++i) + { + job.inputTextures[i] = layerData[i].layerBuffer; + job.inputTextureRects[i] = layerData[i].layerRect; + } job.layersPerJob = layersPerJob; job.flipY = false; combineJob.flipY = true; - int jobCount = buffers.Count / layersPerJob + (buffers.Count % layersPerJob > 0 ? 1 : 0); + var jobCount = layerData.Count / layersPerJob + (layerData.Count % layersPerJob > 0 ? 1 : 0); combineJob.layersPerJob = jobCount; - NativeArray[] premergedBuffer = new NativeArray[jobCount]; - job.output = new NativeArray(jobCount, Allocator.TempJob); - combineJob.buffers = new NativeArray(jobCount, Allocator.TempJob); - - for (int i = 0; i < jobCount; ++i) + var premergedBuffer = new NativeArray[jobCount]; + job.outputTextureSizes = new NativeArray(jobCount, Allocator.TempJob); + job.outputTextures = new NativeArray(jobCount, Allocator.TempJob); + combineJob.inputTextures = new NativeArray(jobCount, Allocator.TempJob); + combineJob.inputTextureRects = new NativeArray(jobCount, Allocator.TempJob); + + for (var i = 0; i < jobCount; ++i) { - premergedBuffer[i] = new NativeArray(width * height * 4, Allocator.TempJob); - job.output[i] = new IntPtr(premergedBuffer[i].GetUnsafePtr()); - combineJob.buffers[i] = new IntPtr(premergedBuffer[i].GetUnsafeReadOnlyPtr()); + premergedBuffer[i] = new NativeArray(documentSize.x * documentSize.y * 4, Allocator.TempJob); + job.outputTextureSizes[i] = new int2(documentSize.x, documentSize.y); + job.outputTextures[i] = new IntPtr(premergedBuffer[i].GetUnsafePtr()); + combineJob.inputTextures[i] = new IntPtr(premergedBuffer[i].GetUnsafeReadOnlyPtr()); + combineJob.inputTextureRects[i] = new int4(0, 0, documentSize.x, documentSize.y); } - combineJob.output = new NativeArray(new[] {new IntPtr(output.GetUnsafePtr()), }, Allocator.TempJob); + + combineJob.outputTextureSizes = new NativeArray(new [] {new int2(documentSize.x, documentSize.y) }, Allocator.TempJob); + combineJob.outputTextures = new NativeArray(new[] { new IntPtr(output.GetUnsafePtr()) }, Allocator.TempJob); var handle = job.Schedule(jobCount, 1); combineJob.Schedule(1, 1, handle).Complete(); + foreach (var b in premergedBuffer) { if (b.IsCreated) b.Dispose(); } + UnityEngine.Profiling.Profiler.EndSample(); } - static unsafe void GetBuffersToMergeFromLayer(BitmapLayer layer, bool importHiddenLayer, List buffers) + static unsafe void GetLayerDataToMerge(in PSDExtractLayerData layer, ref List layerData, bool importHiddenLayer) { - if (!layer.Visible && importHiddenLayer == false) + var bitmapLayer = layer.bitmapLayer; + if (!bitmapLayer.Visible && importHiddenLayer == false) return; - if (layer.IsGroup) + + if (bitmapLayer.IsGroup) { - for (int i = layer.ChildLayer.Count - 1; i >= 0; --i) - GetBuffersToMergeFromLayer(layer.ChildLayer[i], importHiddenLayer, buffers); + for (var i = layer.children.Length - 1; i >= 0; --i) + GetLayerDataToMerge(layer.children[i], ref layerData, importHiddenLayer); } - if (layer.Surface != null) - buffers.Add(new IntPtr(layer.Surface.color.GetUnsafeReadOnlyPtr())); - else - Debug.LogWarning(string.Format("Layer {0} has no color buffer", layer.Name)); + + if (bitmapLayer.Surface == null || bitmapLayer.localRect == default) + return; + + var layerRect = bitmapLayer.documentRect; + var data = new LayerData() + { + layerBuffer = new IntPtr(bitmapLayer.Surface.color.GetUnsafeReadOnlyPtr()), + layerRect = new int4(layerRect.X, layerRect.Y, layerRect.Width, layerRect.Height) + }; + layerData.Add(data); } + [BurstCompile] struct FlattenImageInternalJob : IJobParallelFor { - [ReadOnly] - [DeallocateOnJobCompletion] - public NativeArray buffers; + [ReadOnly, DeallocateOnJobCompletion] + public NativeArray inputTextures; + [ReadOnly, DeallocateOnJobCompletion] + public NativeArray inputTextureRects; [ReadOnly] public int layersPerJob; [ReadOnly] - public int width; - [ReadOnly] - public int height; - [ReadOnly] public bool flipY; + + [ReadOnly, DeallocateOnJobCompletion] + public NativeArray outputTextureSizes; [DeallocateOnJobCompletion] - public NativeArray output; + public NativeArray outputTextures; public unsafe void Execute(int index) { - var premerge = (Color32*)output[index].ToPointer(); - for (int layerIndex = index * layersPerJob; layerIndex < (index * layersPerJob) + layersPerJob; ++layerIndex) + var outputColor = (Color32*)outputTextures[index].ToPointer(); + for (var layerIndex = index * layersPerJob; layerIndex < (index * layersPerJob) + layersPerJob; ++layerIndex) { - if (buffers.Length <= layerIndex) + if (inputTextures.Length <= layerIndex) break; - var buffer = (Color32*)buffers[layerIndex].ToPointer(); - for (int i = 0; i < height; ++i) + + var inputColor = (Color32*)inputTextures[layerIndex].ToPointer(); + var inPosX = inputTextureRects[layerIndex].x; + var inPosY = inputTextureRects[layerIndex].y; + var inWidth = inputTextureRects[layerIndex].z; + var inHeight = inputTextureRects[layerIndex].w; + + var outWidth = outputTextureSizes[index].x; + var outHeight = outputTextureSizes[index].y; + + for (var y = 0; y < inHeight; ++y) { - int sourceYIndex = i * width; - int destYIndex = flipY ? (height - 1 - i) * width : sourceYIndex; - for (int j = 0; j < width; ++j) + var inY = y * inWidth; + var outY = flipY ? (outHeight - 1 - y - inPosY) * outWidth : (y + inPosY) * outWidth; + + for (var x = 0; x < inWidth; ++x) { - int sourceIndex = sourceYIndex + j; - int destIndex = destYIndex + j; - Color sourceColor = buffer[sourceIndex]; - Color destColor = premerge[destIndex]; - Color finalColor = new Color(); - - var destAlpha = destColor.a * (1 - sourceColor.a); - finalColor.a = sourceColor.a + destColor.a * (1 - sourceColor.a); - var premultiplyAlpha = 1 / finalColor.a; - finalColor.r = (sourceColor.r * sourceColor.a + destColor.r * destAlpha) * premultiplyAlpha; - finalColor.g = (sourceColor.g * sourceColor.a + destColor.g * destAlpha) * premultiplyAlpha; - finalColor.b = (sourceColor.b * sourceColor.a + destColor.b * destAlpha) * premultiplyAlpha; - - premerge[destIndex] = finalColor; + var inX = inY + x; + var outX = outY + x + inPosX; + + Color inColor = inputColor[inX]; + Color prevOutColor = outputColor[outX]; + var outColor = new Color(); + + var destAlpha = prevOutColor.a * (1 - inColor.a); + outColor.a = inColor.a + prevOutColor.a * (1 - inColor.a); + var premultiplyAlpha = 1 / outColor.a; + outColor.r = (inColor.r * inColor.a + prevOutColor.r * destAlpha) * premultiplyAlpha; + outColor.g = (inColor.g * inColor.a + prevOutColor.g * destAlpha) * premultiplyAlpha; + outColor.b = (inColor.b * inColor.a + prevOutColor.b * destAlpha) * premultiplyAlpha; + + outputColor[outX] = outColor; } } } } } } -} +} \ No newline at end of file diff --git a/Editor/Unity.2D.Psdimporter.Editor.asmdef b/Editor/Unity.2D.Psdimporter.Editor.asmdef index b036028..bb70c8a 100644 --- a/Editor/Unity.2D.Psdimporter.Editor.asmdef +++ b/Editor/Unity.2D.Psdimporter.Editor.asmdef @@ -8,6 +8,8 @@ "Unity.2D.Animation.Editor", "Unity.2D.Animation.Runtime", "Unity.2D.Sprite.Editor", + "Unity.Mathematics", + "Unity.Burst", "Unity.InternalAPIEngineBridge.001" ], "includePlatforms": [ @@ -19,6 +21,12 @@ "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [], - "versionDefines": [], + "versionDefines": [ + { + "name": "Unity", + "expression": "2021.3.9f1", + "define": "USE_TEXTURE_PLATFORM_FIX" + } + ], "noEngineReferences": false } \ No newline at end of file diff --git a/package.json b/package.json index dcbfee8..8356afc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.unity.2d.psdimporter", - "version": "6.0.5", + "version": "6.0.6", "unity": "2021.2", "unityRelease": "7f1", "displayName": "2D PSD Importer", @@ -12,22 +12,19 @@ ], "category": "2D", "dependencies": { - "com.unity.2d.animation": "7.0.7", - "com.unity.2d.common": "6.0.4", + "com.unity.2d.animation": "7.0.8", + "com.unity.2d.common": "6.0.5", "com.unity.2d.sprite": "1.0.0" }, "relatedPackages": { - "com.unity.2d.psdimporter.tests": "6.0.5" - }, - "_upm": { - "changelog": "### Changed\n- Internal code clean up." + "com.unity.2d.psdimporter.tests": "6.0.6" }, "upmCi": { - "footprint": "6560d6e400c14267308f56f31fcda6aadab25f8c" + "footprint": "f4b0d1b01e9e1dfacf581b2289f407d1f3f28a13" }, "repository": { "url": "https://github.cds.internal.unity3d.com/unity/2d.git", "type": "git", - "revision": "21cf4d65740fb2e92caf175a7fcaea68198fa577" + "revision": "d1ad42baef5a9bf44369c855eb89f86d6cd3c744" } }