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