From a8904de90b4e26b9f53616c5f34f9c01e022eae6 Mon Sep 17 00:00:00 2001 From: Unity Technologies <@unity> Date: Wed, 30 Nov 2022 00:00:00 +0000 Subject: [PATCH] com.unity.2d.psdimporter@9.0.0-pre.2 ## [9.0.0-pre.2] - 2022-11-30 ### Fixed - Enabled the ability to select mipmap streaming for textures imported with the PSD Importer. - Fixed an issue where the amount of alpha removed from layers would not be re-applied as final position offset of the layers. - Fixed an issue where the editor would crash when importing .psd/.psb files with their layers outside of the document canvas. (Case DANB-300) - Fixed an issue where the generated GameObjects would be laid out differently from how they appear in the DCC tool. (Case DANB-298) --- CHANGELOG.md | 7 + Documentation~/PSD-importer-properties.md | 2 +- Editor/PSDImporter.cs | 23 +- Editor/PSDImporterAPI.cs | 27 ++ Editor/PSDImporterDataProvider.cs | 2 +- Editor/PSDImporterEditor.cs | 35 +++ Editor/PSDLayer.cs | 19 ++ Editor/Tasks/ExtractLayerTask.cs | 332 ++++++++++++++-------- Editor/Tasks/FlattenImageTask.cs | 35 ++- package.json | 12 +- 10 files changed, 343 insertions(+), 151 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77ad0e0..5aa6686 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [9.0.0-pre.2] - 2022-11-30 +### Fixed +- Enabled the ability to select mipmap streaming for textures imported with the PSD Importer. +- Fixed an issue where the amount of alpha removed from layers would not be re-applied as final position offset of the layers. +- Fixed an issue where the editor would crash when importing .psd/.psb files with their layers outside of the document canvas. (Case DANB-300) +- Fixed an issue where the generated GameObjects would be laid out differently from how they appear in the DCC tool. (Case DANB-298) + ## [9.0.0-pre.1] - 2022-09-21 ### Changed - Simplified the Sprite Meta Data storage. We now have 3 storages; Single Sprite, Multiple Sprites and Mosaiac (Atlased) Sprites. diff --git a/Documentation~/PSD-importer-properties.md b/Documentation~/PSD-importer-properties.md index 18ef391..701aa02 100644 --- a/Documentation~/PSD-importer-properties.md +++ b/Documentation~/PSD-importer-properties.md @@ -153,7 +153,7 @@ To include and maintain the group and hierarchy structure as per the source file ![](images/as-per-source-22.2.png)
The generated Prefab of the same source file with **Layer Group** set to **As Per Source File**. ###Character Rig -This section is only available if the **Texture Type** is set to **Multiple** and **Import Mode** is set to **Individual Sprites (Mosaic)**. +This section is only available if the **Texture Type** is set to **Multiple**, **Import Mode** is set to **Individual Sprites (Mosaic)** and the [2D Animation package](https://docs.unity3d.com/Packages/com.unity.2d.animation@latest) is installed. ![](images/character-rig-22.2.png) diff --git a/Editor/PSDImporter.cs b/Editor/PSDImporter.cs index 0ccb1a7..995a201 100644 --- a/Editor/PSDImporter.cs +++ b/Editor/PSDImporter.cs @@ -22,7 +22,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(23100001, new string[] {"psb"}, new[] {"psd"}, AllowCaching = true)] + [ScriptedImporter(23100002, new string[] {"psb"}, new[] {"psd"}, AllowCaching = true)] [HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.psdimporter@latest")] [MovedFrom("UnityEditor.Experimental.AssetImporters")] public partial class PSDImporter : ScriptedImporter, ISpriteEditorDataProvider @@ -52,7 +52,8 @@ internal enum ELayerMappingOption readable = false, #if ENABLE_TEXTURE_STREAMING - streamingMipmaps = true, + streamingMipmaps = false, + streamingMipmapsPriority = 0, #endif fadeOut = false, @@ -512,7 +513,7 @@ TextureGenerationOutput ImportFlattenImage(Document doc, AssetImportContext ctx) var outputImageBuffer = new NativeArray(doc.width * doc.height, Allocator.Persistent); try { - FlattenImageTask.Execute(m_ExtractData, ref outputImageBuffer, m_ImportHiddenLayers, documentSize); + FlattenImageTask.Execute(m_ExtractData, ref outputImageBuffer, m_ImportHiddenLayers, canvasSize); m_SingleSpriteImportData[0].name = System.IO.Path.GetFileNameWithoutExtension(ctx.assetPath) + "_1"; m_SingleSpriteImportData[0].alignment = (SpriteAlignment)m_TextureImporterSettings.spriteAlignment; @@ -548,7 +549,7 @@ TextureGenerationOutput ImportFromLayers(AssetImportContext ctx) List psdLayers = null; try { - ExtractLayerTask.Execute(in m_ExtractData, out psdLayers, m_ImportHiddenLayers, documentSize); + ExtractLayerTask.Execute(in m_ExtractData, out psdLayers, m_ImportHiddenLayers, canvasSize); var mappingStrategy = GetLayerMappingStrategy(); var layerUnique = mappingStrategy.LayersUnique(psdLayers.ConvertAll(x => (IPSDLayerMappingStrategyComparable)x)); @@ -595,6 +596,10 @@ TextureGenerationOutput ImportFromLayers(AssetImportContext ctx) } ImagePacker.Pack(layerBuffers.ToArray(), layerWidth.ToArray(), layerHeight.ToArray(), m_Padding, m_SpriteSizeExpand, out outputImageBuffer, out int width, out int height, out RectInt[] spriteData, out Vector2Int[] uvTransform); + + var packOffsets = new Vector2[spriteData.Length]; + for (var i = 0; i < packOffsets.Length; ++i) + packOffsets[i] = new Vector2((uvTransform[i].x - spriteData[i].position.x) / -1f, (uvTransform[i].y - spriteData[i].position.y) / -1f); var spriteImportData = GetSpriteImportData(); if (spriteImportData.Count <= 0 || shouldResliceFromLayer || hasNewLayer) @@ -623,12 +628,11 @@ TextureGenerationOutput ImportFromLayers(AssetImportContext ctx) psdLayer.spriteName = ImportUtilities.GetUniqueSpriteName(psdLayer.name, spriteNameHash, m_KeepDupilcateSpriteName); spriteSheet.name = psdLayer.spriteName; - spriteSheet.spritePosition = psdLayer.layerPosition; + spriteSheet.spritePosition = psdLayer.layerPosition + packOffsets[i]; if(shouldResliceFromLayer) spriteSheet.rect = new Rect(spriteData[i].x, spriteData[i].y, spriteData[i].width, spriteData[i].height); - - + spriteSheet.uvTransform = uvTransform[i]; psdLayer.spriteID = spriteSheet.spriteID; @@ -697,8 +701,9 @@ TextureGenerationOutput ImportFromLayers(AssetImportContext ctx) r.width = spriteData[k].width; r.height = spriteData[k].height; } + spriteSheet.rect = r; - spriteSheet.spritePosition = psdLayers[i].layerPosition; + spriteSheet.spritePosition = psdLayers[i].layerPosition + packOffsets[k]; } if (spriteSheet != null) @@ -1284,7 +1289,7 @@ internal void SetDocumentPivot(Vector2 pivot) SpriteImportMode.None : (SpriteImportMode)m_TextureImporterSettings.spriteMode; - internal Vector2Int documentSize => importData.documentSize; + internal Vector2Int canvasSize => importData.documentSize; #if ENABLE_2D_ANIMATION internal CharacterData characterData diff --git a/Editor/PSDImporterAPI.cs b/Editor/PSDImporterAPI.cs index 75970e5..2edddb2 100644 --- a/Editor/PSDImporterAPI.cs +++ b/Editor/PSDImporterAPI.cs @@ -128,6 +128,33 @@ public int mipmapFadeDistanceStart } } + /// + /// Enable mipmap streaming for the texture. + ///

Only load larger mipmaps as needed to render the current game cameras. Requires texture streaming to be enabled in quality settings. + ///
+ public bool streamingMipmaps + { + get => m_TextureImporterSettings.streamingMipmaps; + set + { + m_TextureImporterSettings.streamingMipmaps = value; + SetDirty(); + } + } + + /// + /// Mipmap streaming priority when there's contention for resources. Positive numbers represent higher priority. Valid range is -128 to 127. + /// + public int streamingMipmapsPriority + { + get => m_TextureImporterSettings.streamingMipmapsPriority; + set + { + m_TextureImporterSettings.streamingMipmapsPriority = Mathf.Clamp(value, -128, 127); + SetDirty(); + } + } + /// /// Mip level where texture is faded out completely. /// diff --git a/Editor/PSDImporterDataProvider.cs b/Editor/PSDImporterDataProvider.cs index 8961b16..501d758 100644 --- a/Editor/PSDImporterDataProvider.cs +++ b/Editor/PSDImporterDataProvider.cs @@ -285,7 +285,7 @@ public CharacterData GetCharacterData() parts.Reverse(); cd.parts = parts.ToArray(); - cd.dimension = dataProvider.documentSize; + cd.dimension = dataProvider.canvasSize; cd.characterGroups = groups.ToArray(); return cd; } diff --git a/Editor/PSDImporterEditor.cs b/Editor/PSDImporterEditor.cs index 7c63e29..379ecfb 100644 --- a/Editor/PSDImporterEditor.cs +++ b/Editor/PSDImporterEditor.cs @@ -47,6 +47,10 @@ struct InspectorGUI SerializedProperty m_sRGBTexture; SerializedProperty m_AlphaSource; SerializedProperty m_Swizzle; +#if ENABLE_TEXTURE_STREAMING + SerializedProperty m_StreamingMipmaps; + SerializedProperty m_StreamingMipmapsPriority; +#endif SerializedProperty m_MipMapMode; SerializedProperty m_EnableMipMap; SerializedProperty m_FadeOut; @@ -150,6 +154,10 @@ public override void OnEnable() m_IsReadable = textureImporterSettingsSP.FindPropertyRelative("m_IsReadable"); m_sRGBTexture = textureImporterSettingsSP.FindPropertyRelative("m_sRGBTexture"); m_AlphaSource = textureImporterSettingsSP.FindPropertyRelative("m_AlphaSource"); +#if ENABLE_TEXTURE_STREAMING + m_StreamingMipmaps = textureImporterSettingsSP.FindPropertyRelative("m_StreamingMipmaps"); + m_StreamingMipmapsPriority = textureImporterSettingsSP.FindPropertyRelative("m_StreamingMipmapsPriority"); +#endif m_MipMapMode = textureImporterSettingsSP.FindPropertyRelative("m_MipMapMode"); m_EnableMipMap = textureImporterSettingsSP.FindPropertyRelative("m_EnableMipMap"); m_Swizzle = textureImporterSettingsSP.FindPropertyRelative("m_Swizzle"); @@ -1093,6 +1101,22 @@ void MipMapGUI() { EditorGUI.indentLevel++; ToggleFromInt(m_BorderMipMap, styles.borderMipMaps); + +#if ENABLE_TEXTURE_STREAMING + ToggleFromInt(m_StreamingMipmaps, styles.streamingMipMaps); + if (m_StreamingMipmaps.boolValue && !m_StreamingMipmaps.hasMultipleDifferentValues) + { + EditorGUI.indentLevel++; + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(m_StreamingMipmapsPriority, styles.streamingMipmapsPriority); + if (EditorGUI.EndChangeCheck()) + { + m_StreamingMipmapsPriority.intValue = Mathf.Clamp(m_StreamingMipmapsPriority.intValue, -128, 127); + } + EditorGUI.indentLevel--; + } +#endif + m_MipMapMode.intValue = EditorGUILayout.Popup(styles.mipMapFilter, m_MipMapMode.intValue, styles.mipMapFilterOptions); ToggleFromInt(m_MipMapsPreserveCoverage, styles.mipMapsPreserveCoverage); @@ -1336,6 +1360,13 @@ internal TextureImporterSettings GetSerializedPropertySettings(TextureImporterSe if (!m_BorderMipMap.hasMultipleDifferentValues) settings.borderMipmap = m_BorderMipMap.intValue > 0; +#if ENABLE_TEXTURE_STREAMING + if (!m_StreamingMipmaps.hasMultipleDifferentValues) + settings.streamingMipmaps = m_StreamingMipmaps.intValue > 0; + if (!m_StreamingMipmapsPriority.hasMultipleDifferentValues) + settings.streamingMipmapsPriority = m_StreamingMipmapsPriority.intValue; +#endif + if (!m_MipMapsPreserveCoverage.hasMultipleDifferentValues) settings.mipMapsPreserveCoverage = m_MipMapsPreserveCoverage.intValue > 0; @@ -1558,6 +1589,10 @@ internal class Styles 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"); +#if ENABLE_TEXTURE_STREAMING + public readonly GUIContent streamingMipMaps = EditorGUIUtility.TrTextContent("Mip Streaming", "Only load larger mipmaps as needed to render the current game cameras. Requires texture streaming to be enabled in quality settings."); + public readonly GUIContent streamingMipmapsPriority = EditorGUIUtility.TrTextContent("Priority", "Mipmap streaming priority when there's contention for resources. Positive numbers represent higher priority. Valid range is -128 to 127."); +#endif 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"); diff --git a/Editor/PSDLayer.cs b/Editor/PSDLayer.cs index d792cd6..3d0a5ec 100644 --- a/Editor/PSDLayer.cs +++ b/Editor/PSDLayer.cs @@ -48,6 +48,25 @@ public PSDLayer(NativeArray tex, int parent, bool group, string layerNa m_SpriteID = new GUID().ToString(); } + public PSDLayer(PSDLayer layer) + { + m_Name = layer.m_Name; + m_SpriteName = layer.m_SpriteName; + m_IsGroup = layer.m_IsGroup; + m_ParentIndex = layer.m_ParentIndex; + m_SpriteID = layer.m_SpriteID; + m_LayerID = layer.m_LayerID; + m_MosaicPosition = layer.m_MosaicPosition; + m_Flatten = layer.m_Flatten; + m_IsImported = layer.m_IsImported; + m_IsVisible = layer.m_IsVisible; + m_LayerPosition = layer.m_LayerPosition; + m_GameObject = layer.m_GameObject; + width = layer.width; + height = layer.height; + texture = layer.texture; + } + public bool isVisible => m_IsVisible; public int layerID { get { return m_LayerID; } private set { m_LayerID = value; } } diff --git a/Editor/Tasks/ExtractLayerTask.cs b/Editor/Tasks/ExtractLayerTask.cs index 88e2205..767fbe6 100644 --- a/Editor/Tasks/ExtractLayerTask.cs +++ b/Editor/Tasks/ExtractLayerTask.cs @@ -20,9 +20,6 @@ struct LayerGroupData /// The layer's bounding box in document space. /// public int4 documentRect { get; set; } - - public int width => documentRect.z; - public int height => documentRect.w; } [BurstCompile] @@ -36,75 +33,87 @@ struct ConvertBufferJob : IJobParallelFor public NativeArray inputLayerRects; [ReadOnly, DeallocateOnJobCompletion] public NativeArray layerGroupDataData; - + [ReadOnly, DeallocateOnJobCompletion] - public NativeArray outputTextureSizes; + public NativeArray outputLayerRect; [DeallocateOnJobCompletion] public NativeArray outputTextures; - public unsafe void Execute(int index) + public unsafe void Execute(int groupIndex) { - 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; + var outputColor = (Color32*)outputTextures[groupIndex]; + var groupStartIndex = layerGroupDataData[groupIndex].startIndex; + var groupEndIndex = layerGroupDataData[groupIndex].endIndex; + + var outStartX = outputLayerRect[groupIndex].x; + var outStartY = outputLayerRect[groupIndex].y; + var outWidth = outputLayerRect[groupIndex].z; + var outHeight = outputLayerRect[groupIndex].w; - for (var i = groupEndIndex; i >= groupStartIndex; --i) + for (var layerIndex = groupEndIndex; layerIndex >= groupStartIndex; --layerIndex) { - if (inputTextures[i] == IntPtr.Zero) + if (inputTextures[layerIndex] == IntPtr.Zero) continue; - 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; + var inputColor = (Color32*)inputTextures[layerIndex]; + var inX = inputLayerRects[layerIndex].x; + var inY = inputLayerRects[layerIndex].y; + var inWidth = inputLayerRects[layerIndex].z; + var inHeight = inputLayerRects[layerIndex].w; for (var y = 0; y < inHeight; ++y) { - var inY = y * inWidth; - var outY = (outHeight - 1 - y - layerToGroupSpace.y) * outWidth; + var outPosY = (y + inY) - outStartY; + // If pixel is outside of output texture's Y, move to the next pixel. + if (outPosY < 0 || outPosY >= outHeight) + continue; + // Flip Y position on the input texture, because + // PSDs textures are stored "upside-down" + var inRow = ((inHeight - 1) - y) * inWidth; + var outRow = outPosY * outWidth; + for (var x = 0; x < inWidth; ++x) { - var inX = inY + x; - var outX = outY + x + layerToGroupSpace.x; - - if (inX >= inputTextureBufferSizes[i]) - break; - if (outX >= (outWidth * outHeight)) - break; + var outPosX = (x + inX) - outStartX; + // If pixel is outside of output texture's X, move to the next pixel. + if (outPosX < 0 || outPosX >= outWidth) + continue; - Color inColor = inputColor[inX]; - Color prevOutColor = outputColor[outX]; + var inBufferIndex = inRow + x; + var outBufferIndex = outRow + outPosX; + if (outBufferIndex < 0 || outBufferIndex > (outWidth * outHeight)) + continue; + + Color inColor = inputColor[inBufferIndex]; + Color prevOutColor = outputColor[outBufferIndex]; 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; + + var premultiplyAlpha = outColor.a > 0.0f ? 1 / outColor.a : 1f; 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; + + outputColor[outBufferIndex] = outColor; } } } } } - public static unsafe void Execute(in PSDExtractLayerData[] inputLayers, out List outputLayers, bool importHiddenLayer, Vector2Int documentSize) + public static unsafe void Execute(in PSDExtractLayerData[] psdExtractLayerData, out List outputLayers, bool importHiddenLayer, Vector2Int canvasSize) { outputLayers = new List(); UnityEngine.Profiling.Profiler.BeginSample("ExtractLayer_PrepareJob"); + + var inputLayers = new List(); + ExtractLayerData(in psdExtractLayerData, ref inputLayers, importHiddenLayer, false, true, canvasSize); var layerGroupData = new List(); - ExtractLayer(in inputLayers, ref outputLayers, ref layerGroupData, importHiddenLayer, false, true, documentSize); - + GenerateOutputLayers(in inputLayers, ref outputLayers, ref layerGroupData, false, canvasSize); + if (layerGroupData.Count == 0) { foreach (var layer in outputLayers) @@ -113,35 +122,36 @@ public static unsafe void Execute(in PSDExtractLayerData[] inputLayers, out List } var job = new ConvertBufferJob(); - 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.inputTextureBufferSizes = new NativeArray(inputLayers.Count, Allocator.TempJob); + job.inputTextures = new NativeArray(inputLayers.Count, Allocator.TempJob); + job.inputLayerRects = new NativeArray(inputLayers.Count, Allocator.TempJob); + job.outputLayerRect = new NativeArray(layerGroupData.Count, Allocator.TempJob); job.outputTextures = new NativeArray(layerGroupData.Count, Allocator.TempJob); - for (int i = 0, outputLayerIndex = 0; i < outputLayers.Count; ++i) + for (int i = 0, groupIndex = 0; i < inputLayers.Count; ++i) { + var inputLayer = inputLayers[i]; var outputLayer = outputLayers[i]; - job.inputTextures[i] = outputLayer.texture.IsCreated ? new IntPtr(outputLayer.texture.GetUnsafePtr()) : IntPtr.Zero; + job.inputTextures[i] = inputLayer.texture.IsCreated ? new IntPtr(inputLayer.texture.GetUnsafePtr()) : IntPtr.Zero; - if (outputLayerIndex < layerGroupData.Count && layerGroupData[outputLayerIndex].startIndex == i) + var isGroupOwner = groupIndex < layerGroupData.Count && layerGroupData[groupIndex].startIndex == i; + if (isGroupOwner) { - var inputLayer = layerGroupData[outputLayerIndex]; + outputLayer.texture = new NativeArray(outputLayer.width * outputLayer.height, Allocator.Persistent); - 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; + job.outputLayerRect[groupIndex] = new int4((int)outputLayer.layerPosition.x, (int)outputLayer.layerPosition.y, outputLayer.width, outputLayer.height); + job.outputTextures[groupIndex] = outputLayer.texture.IsCreated ? new IntPtr(outputLayer.texture.GetUnsafePtr()) : IntPtr.Zero; + job.inputTextureBufferSizes[i] = inputLayer.texture.IsCreated ? inputLayer.texture.Length : -1; + job.inputLayerRects[i] = layerGroupData[groupIndex].documentRect; + ++groupIndex; } else { - job.inputTextureBufferSizes[i] = outputLayer.texture.IsCreated ? outputLayer.texture.Length : -1; + job.inputTextureBufferSizes[i] = inputLayer.texture.IsCreated ? inputLayer.texture.Length : -1; + job.inputLayerRects[i] = new int4((int)inputLayer.layerPosition.x, (int)inputLayer.layerPosition.y, inputLayer.width, inputLayer.height); outputLayer.texture = default; } - - job.inputLayerRects[i] = new int4((int)outputLayer.layerPosition.x, (int)outputLayer.layerPosition.y, outputLayer.width, outputLayer.height); } job.layerGroupDataData = new NativeArray(layerGroupData.ToArray(), Allocator.TempJob); @@ -152,98 +162,176 @@ public static unsafe void Execute(in PSDExtractLayerData[] inputLayers, out List handle.Complete(); } - static Rect ExtractLayer(in PSDExtractLayerData[] inputLayers, ref List outputLayers, ref List layerGroupData, bool importHiddenLayer, bool flatten, bool parentGroupVisible, Vector2Int documentSize) + static void ExtractLayerData(in PSDExtractLayerData[] inputLayers, ref List extractedLayers, bool importHiddenLayer, bool flatten, bool parentGroupVisible, Vector2Int canvasSize) { - // parent is the previous element in extractedLayer - var parentGroupIndex = outputLayers.Count - 1; - var layerBoundingBox = default(Rect); - + var parentGroupIndex = extractedLayers.Count - 1; + foreach (var inputLayer in inputLayers) { var bitmapLayer = inputLayer.bitmapLayer; var importSettings = inputLayer.importSetting; var layerVisible = bitmapLayer.Visible && parentGroupVisible; + + var layerRect = new RectInt(bitmapLayer.documentRect.X, bitmapLayer.documentRect.Y, bitmapLayer.Surface.width, bitmapLayer.Surface.height); + + if (!bitmapLayer.IsGroup) + layerRect.y = (canvasSize.y - layerRect.y - layerRect.height); - var layerRect = new Rect(float.MaxValue, float.MaxValue, 0f, 0f); - if (inputLayer.bitmapLayer.IsGroup) + NativeArray surface = default; + if ((importHiddenLayer || bitmapLayer.Visible) && + importSettings.importLayer && + bitmapLayer.Surface.color.IsCreated && + bitmapLayer.Surface.color.Length > 0) + surface = bitmapLayer.Surface.color; + + var extractedLayer = new PSDLayer(surface, parentGroupIndex, bitmapLayer.IsGroup, bitmapLayer.Name, layerRect.width, layerRect.height, bitmapLayer.LayerID, bitmapLayer.Visible) { - 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 && importSettings.importLayer; + spriteID = inputLayer.importSetting.spriteId, + flatten = bitmapLayer.IsGroup && inputLayer.importSetting.flatten, + layerPosition = bitmapLayer.IsGroup ? Vector2.zero : layerRect.position + }; + + extractedLayer.isImported = (importHiddenLayer || layerVisible) && !flatten && importSettings.importLayer; + if (extractedLayer.isGroup) + extractedLayer.isImported = extractedLayer.isImported && extractedLayer.flatten; + + extractedLayers.Add(extractedLayer); + + if (inputLayer.children.Length > 0) + ExtractLayerData(in inputLayer.children, ref extractedLayers, importHiddenLayer, flatten || extractedLayer.flatten, layerVisible, canvasSize); + } + } + + static void GenerateOutputLayers(in List inputLayers, ref List outputLayers, ref List layerGroupData, bool flatten, Vector2Int canvasSize) + { + var canvasRect = new RectInt(Vector2Int.zero, canvasSize); + + for (var i = 0; i < inputLayers.Count; ++i) + { + var inputLayer = inputLayers[i]; + + var outputLayer = new PSDLayer(inputLayer); + var outputRect = new RectInt((int)outputLayer.layerPosition.x, (int)outputLayer.layerPosition.y, outputLayer.width, outputLayer.height); + + if (inputLayer.isGroup) + { + var childIndices = FindAllChildrenOfParent(i, in inputLayers); + childIndices.Sort(); - 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; + var startIndex = i; + var endIndex = i + childIndices.Count; - // If this group is to be flatten and there are flatten layers - if (flatten == false && outputLayer.flatten && startIndex < endIndex) + if (flatten == false && inputLayer.flatten && startIndex < endIndex) { + var groupBoundingBox = CalculateLayerRectInChildren(in inputLayers, in childIndices); layerGroupData.Add(new LayerGroupData() { startIndex = startIndex, endIndex = endIndex, - documentRect = new int4((int)layerRect.x, (int)layerRect.y, (int)layerRect.width, (int)layerRect.height) + documentRect = new int4(groupBoundingBox.x, groupBoundingBox.y, groupBoundingBox.width, groupBoundingBox.height) }); - outputLayer.texture = default; - outputLayer.layerPosition = new Vector2(layerRect.x, layerRect.y); - outputLayer.width = (int)layerRect.width; - outputLayer.height = (int)layerRect.height; + outputRect = groupBoundingBox; } } - else + else if(!inputLayer.isGroup && inputLayer.isImported) { - var layerRectDocSpace = bitmapLayer.documentRect; - // From Photoshop "space" into Unity "space" - layerRectDocSpace.Y = (documentSize.y - layerRectDocSpace.Y) - layerRectDocSpace.Height; - - var surface = (importHiddenLayer || bitmapLayer.Visible) && importSettings.importLayer ? 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 && importSettings.importLayer; - outputLayers.Add(outputLayer); - if (outputLayer.isImported) + var inputRect = new int4((int)inputLayer.layerPosition.x, (int)inputLayer.layerPosition.y, inputLayer.width, inputLayer.height); + layerGroupData.Add(new LayerGroupData() { - layerGroupData.Add(new LayerGroupData() - { - startIndex = outputLayers.Count - 1, - endIndex = outputLayers.Count - 1, - documentRect = new int4(layerRectDocSpace.X, layerRectDocSpace.Y, layerRectDocSpace.Width, layerRectDocSpace.Height) - }); - } - - layerRect.x = layerRectDocSpace.X; - layerRect.y = layerRectDocSpace.Y; - layerRect.width = bitmapLayer.Surface.width; - layerRect.height = bitmapLayer.Surface.height; + startIndex = i, + endIndex = i, + documentRect = inputRect + }); } + + CropRect(ref outputRect, canvasRect); + outputLayer.layerPosition = outputRect.position; + outputLayer.width = outputRect.width; + outputLayer.height = outputRect.height; + + outputLayers.Add(outputLayer); + } + } - if (layerBoundingBox == default) - layerBoundingBox = layerRect; - else + static List FindAllChildrenOfParent(int parentIndex, in List layers) + { + var childIndices = new List(); + for (var i = parentIndex + 1; i < layers.Count; ++i) + { + if (layers[i].parentIndex == parentIndex) { - 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; + childIndices.Add(i); + if (layers[i].isGroup) + childIndices.AddRange(FindAllChildrenOfParent(i, in layers)); } + } + return childIndices; + } + + static RectInt CalculateLayerRectInChildren(in List inputLayers, in List childIndices) + { + var groupBoundingBox = default(RectInt); + for (var m = 0; m < childIndices.Count; ++m) + { + var childLayer = inputLayers[childIndices[m]]; + if (childLayer.isGroup) + continue; + + var layerRect = new RectInt((int) childLayer.layerPosition.x, (int) childLayer.layerPosition.y, + childLayer.width, childLayer.height); + if (IsRectIntDefault(groupBoundingBox)) + groupBoundingBox = layerRect; + else + FitRectInsideRect(ref groupBoundingBox, in layerRect); + } + + return groupBoundingBox; + } + + static bool IsRectIntDefault(RectInt rectInt) + { + return rectInt.x == 0 && + rectInt.y == 0 && + rectInt.width == 0 && + rectInt.height == 0; + } - layerBoundingBox.width = Mathf.Min(layerBoundingBox.width, documentSize.x); - layerBoundingBox.height = Mathf.Min(layerBoundingBox.height, documentSize.y); + static void CropRect(ref RectInt baseRect, in RectInt cropArea) + { + if (baseRect.x < cropArea.x) + { + baseRect.width = Mathf.Max(baseRect.width - (cropArea.x - baseRect.x), 0); + baseRect.x = cropArea.x; } - return layerBoundingBox; + if (baseRect.xMax > cropArea.xMax) + { + baseRect.x = Mathf.Min(baseRect.x, cropArea.xMax); + baseRect.width = Mathf.Max(cropArea.xMax - baseRect.x, 0); + } + + if (baseRect.y < cropArea.y) + { + baseRect.height = Mathf.Max(baseRect.height - (cropArea.y - baseRect.y), 0); + baseRect.y = cropArea.y; + } + if (baseRect.yMax > cropArea.yMax) + { + baseRect.y = Mathf.Min(baseRect.y, cropArea.yMax); + baseRect.height = Mathf.Max(cropArea.yMax - baseRect.y, 0); + } + } + + static void FitRectInsideRect(ref RectInt baseRect, in RectInt rectToFitIn) + { + if (baseRect.xMin > rectToFitIn.xMin) + baseRect.xMin = rectToFitIn.xMin; + if (baseRect.yMin > rectToFitIn.yMin) + baseRect.yMin = rectToFitIn.yMin; + if (baseRect.xMax < rectToFitIn.xMax) + baseRect.xMax = rectToFitIn.xMax; + if (baseRect.yMax < rectToFitIn.yMax) + baseRect.yMax = rectToFitIn.yMax; } } -} +} \ No newline at end of file diff --git a/Editor/Tasks/FlattenImageTask.cs b/Editor/Tasks/FlattenImageTask.cs index a051134..c6c73c9 100644 --- a/Editor/Tasks/FlattenImageTask.cs +++ b/Editor/Tasks/FlattenImageTask.cs @@ -72,7 +72,7 @@ public static unsafe void Execute(in PSDExtractLayerData[] layer, ref NativeArra var handle = job.Schedule(jobCount, 1); combineJob.Schedule(1, 1, handle).Complete(); - + foreach (var b in premergedBuffer) { if (b.IsCreated) @@ -133,8 +133,8 @@ public unsafe void Execute(int index) break; var inputColor = (Color32*)inputTextures[layerIndex].ToPointer(); - var inPosX = inputTextureRects[layerIndex].x; - var inPosY = inputTextureRects[layerIndex].y; + var inStartPosX = inputTextureRects[layerIndex].x; + var inStartPosY = inputTextureRects[layerIndex].y; var inWidth = inputTextureRects[layerIndex].z; var inHeight = inputTextureRects[layerIndex].w; @@ -143,26 +143,37 @@ public unsafe void Execute(int index) for (var y = 0; y < inHeight; ++y) { - var inY = y * inWidth; - var outY = flipY ? (outHeight - 1 - y - inPosY) * outWidth : (y + inPosY) * outWidth; + var outPosY = y + inStartPosY; + // If pixel is outside of output texture's Y, move to the next pixel. + if (outPosY < 0 || outPosY >= outHeight) + continue; + var inRow = y * inWidth; + var outRow = flipY ? (outHeight - 1 - y - inStartPosY) * outWidth : (y + inStartPosY) * outWidth; + for (var x = 0; x < inWidth; ++x) { - var inX = inY + x; - var outX = outY + x + inPosX; - - Color inColor = inputColor[inX]; - Color prevOutColor = outputColor[outX]; + var outPosX = x + inStartPosX; + // If pixel is outside of output texture's X, move to the next pixel. + if (outPosX < 0 || outPosX >= outWidth) + continue; + + var inBufferIndex = inRow + x; + var outBufferIndex = outRow + outPosX; + + Color inColor = inputColor[inBufferIndex]; + Color prevOutColor = outputColor[outBufferIndex]; 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; + + var premultiplyAlpha = outColor.a > 0.0f ? 1 / outColor.a : 1f; 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; + outputColor[outBufferIndex] = outColor; } } } diff --git a/package.json b/package.json index 215a0e5..fb6d0f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.unity.2d.psdimporter", - "version": "9.0.0-pre.1", + "version": "9.0.0-pre.2", "unity": "2023.1", "displayName": "2D PSD Importer", "description": "A ScriptedImporter for importing Adobe Photoshop PSB (Photoshop Big) file format. The ScriptedImporter is currently targeted for users who wants to create multi Sprite character animation using Unity 2D Animation Package.", @@ -11,19 +11,19 @@ ], "category": "2D", "dependencies": { - "com.unity.2d.common": "9.0.0-pre.1", + "com.unity.2d.common": "9.0.0-pre.2", "com.unity.2d.sprite": "1.0.0" }, "relatedPackages": { - "com.unity.2d.animation": "10.0.0-pre.1", - "com.unity.2d.psdimporter.tests": "9.0.0-pre.1" + "com.unity.2d.psdimporter.tests": "9.0.0-pre.2" }, "upmCi": { - "footprint": "00b8f3cca9da9e9eab3fb0081d69b109ce111bf8" + "footprint": "05c21630cde4e94d5822721b3889989b6ef9e4a4" }, + "documentationUrl": "https://docs.unity3d.com/Packages/com.unity.2d.psdimporter@9.0/manual/index.html", "repository": { "url": "https://github.cds.internal.unity3d.com/unity/2d.git", "type": "git", - "revision": "6dea8ac5bd6953acd64f1ac9d470912954b0c015" + "revision": "754f72df4d3d05edbd6100a322f8cadb46ba5dab" } }