com.wallstop-studios.unity-helpers 2.0.0-rc73.1 → 2.0.0-rc73.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/Editor/AnimationCopier.cs +58 -50
  2. package/Editor/AnimationCreator.cs +17 -24
  3. package/Editor/AnimationEventEditor.cs +11 -23
  4. package/Editor/FitTextureSizeWindow.cs +9 -9
  5. package/Editor/PrefabChecker.cs +12 -18
  6. package/Editor/SpriteAtlasGenerator.cs +47 -66
  7. package/Editor/SpriteCropper.cs +157 -131
  8. package/Editor/SpriteSettingsApplier.cs +5 -3
  9. package/Editor/Utils/GUIIndentScope.cs +20 -0
  10. package/Editor/Utils/GUIIndentScope.cs.meta +3 -0
  11. package/Runtime/Core/DataStructure/Circle.cs +1 -1
  12. package/Runtime/Core/DataStructure/QuadTree.cs +4 -4
  13. package/Runtime/Core/Extension/ColorExtensions.cs +5 -5
  14. package/Runtime/Core/Extension/IEnumerableExtensions.cs +1 -1
  15. package/Runtime/Core/Extension/UnityExtensions.cs +14 -14
  16. package/Runtime/Core/Helper/Helpers.cs +9 -9
  17. package/Runtime/Core/Helper/Logging/UnityLogTagFormatter.cs +31 -8
  18. package/Runtime/Core/Helper/Partials/ObjectHelpers.cs +2 -2
  19. package/Runtime/Core/Helper/PathHelper.cs +15 -0
  20. package/Runtime/Core/Helper/PathHelper.cs.meta +3 -0
  21. package/Runtime/Core/Random/DotNetRandom.cs +1 -1
  22. package/Runtime/Core/Random/SplitMix64.cs +1 -1
  23. package/Runtime/Core/Random/SquirrelRandom.cs +7 -7
  24. package/Runtime/Core/Random/ThreadLocalRandom.cs +1 -1
  25. package/Runtime/Core/Random/WyRandom.cs +1 -1
  26. package/Runtime/Tags/AttributeEffect.cs +1 -0
  27. package/Runtime/Tags/EffectHandler.cs +1 -1
  28. package/Runtime/UI/LayeredImage.cs +309 -161
  29. package/Runtime/Utils/AnimatorEnumStateMachine.cs +1 -1
  30. package/Runtime/Utils/SetTextureImportData.cs +1 -1
  31. package/Runtime/Utils/TextureScale.cs +4 -4
  32. package/Styles/Elements/Progress/ArcedProgressBar.cs +345 -0
  33. package/Styles/Elements/Progress/ArcedProgressBar.cs.meta +3 -0
  34. package/Styles/Elements/Progress/CircularProgressBar.cs +307 -0
  35. package/Styles/Elements/Progress/CircularProgressBar.cs.meta +3 -0
  36. package/Styles/Elements/Progress/GlitchProgressBar.cs +416 -0
  37. package/Styles/Elements/Progress/GlitchProgressBar.cs.meta +3 -0
  38. package/Styles/Elements/Progress/LiquidProgressBar.cs +632 -0
  39. package/Styles/Elements/Progress/LiquidProgressBar.cs.meta +3 -0
  40. package/Styles/Elements/Progress/MarchingAntsProgressBar.cs +722 -0
  41. package/Styles/Elements/Progress/MarchingAntsProgressBar.cs.meta +3 -0
  42. package/Styles/Elements/Progress/RegularProgressBar.cs +405 -0
  43. package/Styles/Elements/Progress/RegularProgressBar.cs.meta +3 -0
  44. package/Styles/Elements/Progress/WigglyProgressBar.cs +837 -0
  45. package/Styles/Elements/Progress/WigglyProgressBar.cs.meta +3 -0
  46. package/Styles/Elements/Progress.meta +3 -0
  47. package/Styles/Elements.meta +3 -0
  48. package/Styles/USS/ArcedProgressBar.uss +19 -0
  49. package/Styles/USS/ArcedProgressBar.uss.meta +3 -0
  50. package/Styles/USS/CirclularProgressBar.uss +18 -0
  51. package/Styles/USS/CirclularProgressBar.uss.meta +3 -0
  52. package/Styles/USS/RegularProgressBar.uss +33 -0
  53. package/Styles/USS/RegularProgressBar.uss.meta +3 -0
  54. package/Styles/USS/WigglyProgressBar.uss +17 -0
  55. package/Styles/USS/WigglyProgressBar.uss.meta +3 -0
  56. package/Styles/USS.meta +3 -0
  57. package/Styles/WallstopStudios.UnityHelpers.Styles.asmdef +17 -0
  58. package/Styles/WallstopStudios.UnityHelpers.Styles.asmdef.meta +7 -0
  59. package/Styles.meta +3 -0
  60. package/package.json +10 -1
@@ -45,7 +45,7 @@
45
45
  {
46
46
  foreach (Sprite sprite in Sprites)
47
47
  {
48
- if (sprite != null && sprite.rect is { width: > 0, height: > 0 })
48
+ if (sprite is { rect: { width: > 0, height: > 0 } })
49
49
  {
50
50
  TotalArea += (long)(sprite.rect.width * sprite.rect.height);
51
51
  }
@@ -249,7 +249,7 @@
249
249
  Application.dataPath,
250
250
  ""
251
251
  );
252
- if (!string.IsNullOrEmpty(absPath))
252
+ if (!string.IsNullOrWhiteSpace(absPath))
253
253
  {
254
254
  if (absPath.StartsWith(Application.dataPath, StringComparison.Ordinal))
255
255
  {
@@ -295,16 +295,12 @@
295
295
  try
296
296
  {
297
297
  float total = _sourceFolders.Length;
298
- foreach (Object obj in _sourceFolders)
298
+ foreach (Object obj in _sourceFolders.Where(Objects.NotNull))
299
299
  {
300
- if (obj == null)
301
- {
302
- continue;
303
- }
304
-
305
300
  string folderPath = AssetDatabase.GetAssetPath(obj);
306
301
  if (
307
- string.IsNullOrEmpty(folderPath) || !AssetDatabase.IsValidFolder(folderPath)
302
+ string.IsNullOrWhiteSpace(folderPath)
303
+ || !AssetDatabase.IsValidFolder(folderPath)
308
304
  )
309
305
  {
310
306
  this.LogWarn($"Skipping invalid or null source folder entry.");
@@ -312,7 +308,7 @@
312
308
  }
313
309
 
314
310
  string[] guids = AssetDatabase.FindAssets("t:Sprite", new[] { folderPath });
315
- for (int i = 0; i < guids.Length; i++)
311
+ for (int i = 0; i < guids.Length; ++i)
316
312
  {
317
313
  EditorUtility.DisplayProgressBar(
318
314
  Name,
@@ -329,14 +325,8 @@
329
325
  continue;
330
326
  }
331
327
 
332
- IEnumerable<Sprite> sprites = assets.OfType<Sprite>();
333
- foreach (Sprite sp in sprites)
328
+ foreach (Sprite sp in assets.OfType<Sprite>().Where(Objects.NotNull))
334
329
  {
335
- if (sp == null)
336
- {
337
- continue;
338
- }
339
-
340
330
  _totalCount++;
341
331
  if (regex.IsMatch(sp.name))
342
332
  {
@@ -369,11 +359,7 @@
369
359
  return;
370
360
  }
371
361
 
372
- if (
373
- _sourceFolders == null
374
- || _sourceFolders.Length == 0
375
- || _sourceFolders.All(f => f == null)
376
- )
362
+ if (_sourceFolders == null || Array.TrueForAll(_sourceFolders, Objects.Null))
377
363
  {
378
364
  this.LogError($"No valid source folders specified.");
379
365
  EditorUtility.ClearProgressBar();
@@ -386,7 +372,10 @@
386
372
  {
387
373
  string parent = Path.GetDirectoryName(_outputFolder);
388
374
  string newFolderName = Path.GetFileName(_outputFolder);
389
- if (string.IsNullOrEmpty(parent) || string.IsNullOrEmpty(newFolderName))
375
+ if (
376
+ string.IsNullOrWhiteSpace(parent)
377
+ || string.IsNullOrWhiteSpace(newFolderName)
378
+ )
390
379
  {
391
380
  this.LogError($"Output folder path '{_outputFolder}' is invalid.");
392
381
  EditorUtility.ClearProgressBar();
@@ -415,7 +404,7 @@
415
404
  string[] existing = AssetDatabase
416
405
  .FindAssets("t:SpriteAtlas", new[] { _outputFolder })
417
406
  .Select(AssetDatabase.GUIDToAssetPath)
418
- .Where(p => !string.IsNullOrEmpty(p))
407
+ .Where(path => !string.IsNullOrWhiteSpace(path))
419
408
  .ToArray();
420
409
 
421
410
  if (existing.Length > 0)
@@ -439,7 +428,7 @@
439
428
  Regex regex;
440
429
  try
441
430
  {
442
- regex = new(_nameRegex);
431
+ regex = new Regex(_nameRegex);
443
432
  }
444
433
  catch (ArgumentException ex)
445
434
  {
@@ -455,13 +444,8 @@
455
444
  _sourceFolders.Length > 0 ? 0.2f / _sourceFolders.Length : 0f;
456
445
  float sourceFolderProgress = 0.1f;
457
446
 
458
- foreach (Object sourceDirectory in _sourceFolders)
447
+ foreach (Object sourceDirectory in _sourceFolders.Where(Objects.NotNull))
459
448
  {
460
- if (sourceDirectory == null)
461
- {
462
- continue;
463
- }
464
-
465
449
  string folderPath = AssetDatabase.GetAssetPath(sourceDirectory);
466
450
  if (!AssetDatabase.IsValidFolder(folderPath))
467
451
  {
@@ -485,7 +469,7 @@
485
469
  )
486
470
  {
487
471
  string assetPath = AssetDatabase.GUIDToAssetPath(assetGuid);
488
- if (string.IsNullOrEmpty(assetPath))
472
+ if (string.IsNullOrWhiteSpace(assetPath))
489
473
  {
490
474
  continue;
491
475
  }
@@ -496,13 +480,8 @@
496
480
  continue;
497
481
  }
498
482
 
499
- foreach (Sprite sub in allAssets.OfType<Sprite>())
483
+ foreach (Sprite sub in allAssets.OfType<Sprite>().Where(Objects.NotNull))
500
484
  {
501
- if (sub == null)
502
- {
503
- continue;
504
- }
505
-
506
485
  string assetName = sub.name;
507
486
  if (!regex.IsMatch(assetName))
508
487
  {
@@ -518,14 +497,13 @@
518
497
 
519
498
  int totalChunks = 0;
520
499
  Dictionary<string, List<List<Sprite>>> groupChunks = new();
521
-
522
500
  EditorUtility.DisplayProgressBar(Name, "Calculating chunks...", 0.3f);
523
-
524
501
  foreach (KeyValuePair<string, List<Sprite>> kv in groups)
525
502
  {
526
503
  List<Sprite> spritesInGroup = kv
527
- .Value.Where(s => s != null && s.rect is { width: > 0, height: > 0 })
528
- .OrderByDescending(s => s.rect.width * s.rect.height)
504
+ .Value.Where(Objects.NotNull)
505
+ .Where(sprite => sprite.rect is { width: > 0, height: > 0 })
506
+ .OrderByDescending(sprite => sprite.rect.width * sprite.rect.height)
529
507
  .ToList();
530
508
  if (!spritesInGroup.Any())
531
509
  {
@@ -599,7 +577,7 @@
599
577
  ) in groupChunks
600
578
  )
601
579
  {
602
- for (int i = 0; i < chunksForThisGroup.Count; i++)
580
+ for (int i = 0; i < chunksForThisGroup.Count; ++i)
603
581
  {
604
582
  if (!chunksForThisGroup[i].Any())
605
583
  {
@@ -621,21 +599,21 @@
621
599
  }
622
600
 
623
601
  allInitialCandidates = allInitialCandidates
624
- .OrderByDescending(c => c.TotalArea)
625
- .ThenBy(c => c.CandidateName, StringComparer.Ordinal)
602
+ .OrderByDescending(candidate => candidate.TotalArea)
603
+ .ThenBy(candidate => candidate.CandidateName, StringComparer.Ordinal)
626
604
  .ToList();
627
605
 
628
606
  List<MergeableAtlas> workingAtlases = allInitialCandidates
629
- .Select(c => new MergeableAtlas(
630
- c.OriginalGroupKey,
631
- c.Sprites,
632
- c.CandidateName,
633
- c.TotalArea
607
+ .Select(candidate => new MergeableAtlas(
608
+ candidate.OriginalGroupKey,
609
+ candidate.Sprites,
610
+ candidate.CandidateName,
611
+ candidate.TotalArea
634
612
  ))
635
613
  .ToList();
636
614
  int passNumber = 0;
637
- float mergeOptimizationProgressStart = 0.30f;
638
- float mergeOptimizationProgressRange = 0.50f;
615
+ const float mergeOptimizationProgressStart = 0.3f;
616
+ const float mergeOptimizationProgressRange = 0.5f;
639
617
 
640
618
  while (true)
641
619
  {
@@ -643,7 +621,7 @@
643
621
  bool mergedInThisPass = false;
644
622
  float currentPassProgress =
645
623
  mergeOptimizationProgressStart
646
- + passNumber * (mergeOptimizationProgressRange / 15.0f);
624
+ + passNumber * (mergeOptimizationProgressRange / 15f);
647
625
  EditorUtility.DisplayProgressBar(
648
626
  Name,
649
627
  $"Optimizing atlas count (Pass {passNumber}, {workingAtlases.Count} atlases)...",
@@ -654,13 +632,16 @@
654
632
  );
655
633
 
656
634
  workingAtlases = workingAtlases
657
- .OrderByDescending(a => a.TotalArea)
658
- .ThenBy(a => a.RepresentativeInitialName, StringComparer.Ordinal)
635
+ .OrderByDescending(atlas => atlas.TotalArea)
636
+ .ThenBy(
637
+ atlas => atlas.RepresentativeInitialName,
638
+ StringComparer.Ordinal
639
+ )
659
640
  .ToList();
660
641
 
661
642
  bool[] isSubsumed = new bool[workingAtlases.Count];
662
643
 
663
- for (int i = 0; i < workingAtlases.Count; i++)
644
+ for (int i = 0; i < workingAtlases.Count; ++i)
664
645
  {
665
646
  if (isSubsumed[i])
666
647
  {
@@ -669,14 +650,14 @@
669
650
 
670
651
  MergeableAtlas baseAtlas = workingAtlases[i];
671
652
  string baseRepresentativeKey = baseAtlas
672
- .OriginalGroupKeys.OrderBy(k => k, StringComparer.Ordinal)
653
+ .OriginalGroupKeys.OrderBy(key => key, StringComparer.Ordinal)
673
654
  .First();
674
655
 
675
656
  int bestPartnerIndex = -1;
676
657
  MergeableAtlas bestPartnerObject = null;
677
658
  int currentMinLevenshtein = int.MaxValue;
678
659
 
679
- for (int j = i + 1; j < workingAtlases.Count; j++)
660
+ for (int j = i + 1; j < workingAtlases.Count; ++j)
680
661
  {
681
662
  if (isSubsumed[j])
682
663
  {
@@ -693,7 +674,7 @@
693
674
  }
694
675
 
695
676
  string partnerRepresentativeKey = potentialPartner
696
- .OriginalGroupKeys.OrderBy(k => k, StringComparer.Ordinal)
677
+ .OriginalGroupKeys.OrderBy(key => key, StringComparer.Ordinal)
697
678
  .First();
698
679
  int distance = baseRepresentativeKey.LevenshteinDistance(
699
680
  partnerRepresentativeKey
@@ -754,8 +735,8 @@
754
735
  }
755
736
 
756
737
  finalAtlasesData = workingAtlases
757
- .Select(a => (Name: a.GenerateFinalName(), Sprites: a.Sprites))
758
- .OrderBy(a => a.Name)
738
+ .Select(atlas => (Name: atlas.GenerateFinalName(), atlas.Sprites))
739
+ .OrderBy(atlas => atlas.Name, StringComparer.Ordinal)
759
740
  .ToList();
760
741
  }
761
742
  else
@@ -766,7 +747,7 @@
766
747
  string prefix = chunk.Key;
767
748
  List<List<Sprite>> chunks = chunk.Value;
768
749
  List<(string, List<Sprite>)> finalChunks = new();
769
- for (int i = 0; i < chunks.Count; i++)
750
+ for (int i = 0; i < chunks.Count; ++i)
770
751
  {
771
752
  string atlasName = chunks.Count > 1 ? $"{prefix}_{i}" : prefix;
772
753
  finalChunks.Add((atlasName, chunks[i]));
@@ -775,15 +756,15 @@
775
756
  })
776
757
  .ToList();
777
758
  int chunkIndex = 0;
778
- float atlasCreationProgressStart = 0.45f;
779
- float atlasCreationProgressRange = 0.5f;
759
+ const float atlasCreationProgressStart = 0.45f;
760
+ const float atlasCreationProgressRange = 0.5f;
780
761
 
781
762
  foreach ((string prefix, List<List<Sprite>> chunks) in groupChunks)
782
763
  {
783
- for (int i = 0; i < chunks.Count; i++)
764
+ for (int i = 0; i < chunks.Count; ++i)
784
765
  {
785
766
  List<Sprite> chunk = chunks[i];
786
- if (chunk == null || chunk.Count == 0)
767
+ if (chunk is not { Count: > 0 })
787
768
  {
788
769
  continue;
789
770
  }
@@ -30,7 +30,7 @@
30
30
  private const float AlphaThreshold = 0.01f;
31
31
 
32
32
  [SerializeField]
33
- private Object[] _inputDirectories;
33
+ private List<Object> _inputDirectories = new();
34
34
 
35
35
  [SerializeField]
36
36
  private bool _onlyNecessary;
@@ -44,12 +44,56 @@
44
44
  {
45
45
  GUILayout.Label("Drag folders below", EditorStyles.boldLabel);
46
46
  SerializedObject so = new(this);
47
+ so.Update();
47
48
  SerializedProperty dirs = so.FindProperty(nameof(_inputDirectories));
48
49
  EditorGUILayout.PropertyField(dirs, true);
49
50
  SerializedProperty onlyNecessary = so.FindProperty(nameof(_onlyNecessary));
50
51
  EditorGUILayout.PropertyField(onlyNecessary, true);
51
52
  so.ApplyModifiedProperties();
52
53
 
54
+ if (GUILayout.Button("Select Input Folder"))
55
+ {
56
+ string path = EditorUtility.OpenFolderPanel(
57
+ "Select Sprite Input Folder",
58
+ Application.dataPath,
59
+ ""
60
+ );
61
+ if (!string.IsNullOrWhiteSpace(path))
62
+ {
63
+ if (path.StartsWith(Application.dataPath, StringComparison.Ordinal))
64
+ {
65
+ path = "Assets" + path.Substring(Application.dataPath.Length);
66
+ if (
67
+ !_inputDirectories
68
+ .Select(AssetDatabase.GetAssetPath)
69
+ .Any(directory =>
70
+ string.Equals(
71
+ directory,
72
+ path,
73
+ StringComparison.OrdinalIgnoreCase
74
+ )
75
+ )
76
+ )
77
+ {
78
+ Object folder = AssetDatabase.LoadAssetAtPath<Object>(path);
79
+ if (folder == null)
80
+ {
81
+ return;
82
+ }
83
+ _inputDirectories.Add(folder);
84
+ }
85
+ }
86
+ else
87
+ {
88
+ EditorUtility.DisplayDialog(
89
+ "Invalid Folder",
90
+ "Please select a folder inside the project's Assets directory.",
91
+ "OK"
92
+ );
93
+ }
94
+ }
95
+ }
96
+
53
97
  if (GUILayout.Button("Find Sprites To Process"))
54
98
  {
55
99
  FindFilesToProcess();
@@ -79,7 +123,7 @@
79
123
  private void FindFilesToProcess()
80
124
  {
81
125
  _filesToProcess = new List<string>();
82
- if (_inputDirectories == null || _inputDirectories.Length == 0)
126
+ if (_inputDirectories is not { Count: > 0 })
83
127
  {
84
128
  this.LogWarn($"No input directories selected.");
85
129
  return;
@@ -118,7 +162,7 @@
118
162
 
119
163
  private void ProcessFoundSprites()
120
164
  {
121
- if (_filesToProcess == null || _filesToProcess.Count == 0)
165
+ if (_filesToProcess is not { Count: > 0 })
122
166
  {
123
167
  this.LogWarn($"No files found or selected for processing.");
124
168
  return;
@@ -169,11 +213,6 @@
169
213
  newImporters.Add(newImporter);
170
214
  }
171
215
  }
172
-
173
- foreach (TextureImporter newImporter in newImporters)
174
- {
175
- newImporter.SaveAndReimport();
176
- }
177
216
  }
178
217
  finally
179
218
  {
@@ -209,7 +248,7 @@
209
248
  }
210
249
 
211
250
  TextureImporter importer = AssetImporter.GetAtPath(assetPath) as TextureImporter;
212
- if (importer == null || !importer.textureType.Equals(TextureImporterType.Sprite))
251
+ if (importer is not { textureType: TextureImporterType.Sprite })
213
252
  {
214
253
  return;
215
254
  }
@@ -232,8 +271,10 @@
232
271
  return null;
233
272
  }
234
273
 
235
- TextureImporter importer = AssetImporter.GetAtPath(assetPath) as TextureImporter;
236
- if (importer == null || !importer.textureType.Equals(TextureImporterType.Sprite))
274
+ if (
275
+ AssetImporter.GetAtPath(assetPath)
276
+ is not TextureImporter { textureType: TextureImporterType.Sprite } importer
277
+ )
237
278
  {
238
279
  return null;
239
280
  }
@@ -241,7 +282,6 @@
241
282
  TextureImporterSettings originalSettings = new();
242
283
  importer.ReadTextureSettings(originalSettings);
243
284
 
244
- bool originalReadableState = importer.isReadable;
245
285
  if (!importer.isReadable)
246
286
  {
247
287
  importer.isReadable = true;
@@ -254,142 +294,128 @@
254
294
  return null;
255
295
  }
256
296
 
257
- TextureImporter resultImporter;
258
- try
259
- {
260
- Color32[] pixels = tex.GetPixels32();
261
-
262
- int width = tex.width;
263
- int height = tex.height;
264
- int minX = width;
265
- int minY = height;
266
- int maxX = 0;
267
- int maxY = 0;
268
- bool hasVisible = false;
269
-
270
- object lockObject = new();
271
-
272
- Parallel.For(
273
- 0,
274
- width * height,
275
- () => (minX: width, minY: height, maxX: 0, maxY: 0, hasVisible: false),
276
- (index, _, localState) =>
277
- {
278
- int x = index % width;
279
- int y = index / width;
297
+ Color32[] pixels = tex.GetPixels32();
298
+ int width = tex.width;
299
+ int height = tex.height;
300
+ int minX = width;
301
+ int minY = height;
302
+ int maxX = 0;
303
+ int maxY = 0;
304
+ bool hasVisible = false;
305
+
306
+ object lockObject = new();
307
+
308
+ Parallel.For(
309
+ 0,
310
+ width * height,
311
+ () => (minX: width, minY: height, maxX: 0, maxY: 0, hasVisible: false),
312
+ (index, _, localState) =>
313
+ {
314
+ int x = index % width;
315
+ int y = index / width;
280
316
 
281
- float a = pixels[index].a / 255f;
282
- if (a > AlphaThreshold)
283
- {
284
- localState.hasVisible = true;
285
- localState.minX = Mathf.Min(localState.minX, x);
286
- localState.minY = Mathf.Min(localState.minY, y);
287
- localState.maxX = Mathf.Max(localState.maxX, x);
288
- localState.maxY = Mathf.Max(localState.maxY, y);
289
- }
290
- return localState;
291
- },
292
- finalLocalState =>
317
+ float a = pixels[index].a / 255f;
318
+ if (a > AlphaThreshold)
293
319
  {
294
- if (finalLocalState.hasVisible)
295
- {
296
- lock (lockObject)
297
- {
298
- hasVisible = true;
299
- minX = Mathf.Min(minX, finalLocalState.minX);
300
- minY = Mathf.Min(minY, finalLocalState.minY);
301
- maxX = Mathf.Max(maxX, finalLocalState.maxX);
302
- maxY = Mathf.Max(maxY, finalLocalState.maxY);
303
- }
304
- }
320
+ localState.hasVisible = true;
321
+ localState.minX = Mathf.Min(localState.minX, x);
322
+ localState.minY = Mathf.Min(localState.minY, y);
323
+ localState.maxX = Mathf.Max(localState.maxX, x);
324
+ localState.maxY = Mathf.Max(localState.maxY, y);
305
325
  }
306
- );
307
-
308
- if (!hasVisible)
309
- {
310
- return null;
311
- }
312
-
313
- int cropWidth = maxX - minX + 1;
314
- int cropHeight = maxY - minY + 1;
315
-
316
- if (_onlyNecessary && cropWidth == width && cropHeight == height)
326
+ return localState;
327
+ },
328
+ finalLocalState =>
317
329
  {
318
- return null;
319
- }
320
-
321
- Texture2D cropped = new(cropWidth, cropHeight, TextureFormat.RGBA32, false);
322
- Color32[] croppedPixels = new Color32[cropWidth * cropHeight];
323
-
324
- Parallel.For(
325
- 0,
326
- cropHeight,
327
- y =>
330
+ if (finalLocalState.hasVisible)
328
331
  {
329
- int sourceYOffset = (y + minY) * width;
330
- int destYOffset = y * cropWidth;
331
- for (int x = 0; x < cropWidth; ++x)
332
+ lock (lockObject)
332
333
  {
333
- croppedPixels[destYOffset + x] = pixels[sourceYOffset + x + minX];
334
+ hasVisible = true;
335
+ minX = Mathf.Min(minX, finalLocalState.minX);
336
+ minY = Mathf.Min(minY, finalLocalState.minY);
337
+ maxX = Mathf.Max(maxX, finalLocalState.maxX);
338
+ maxY = Mathf.Max(maxY, finalLocalState.maxY);
334
339
  }
335
340
  }
336
- );
337
-
338
- cropped.SetPixels32(croppedPixels);
339
- cropped.Apply();
340
-
341
- string newPath = Path.Combine(
342
- assetDirectory,
343
- CroppedPrefix + Path.GetFileName(assetPath)
344
- );
345
- File.WriteAllBytes(newPath, cropped.EncodeToPNG());
346
- DestroyImmediate(cropped);
347
- AssetDatabase.ImportAsset(newPath);
348
- TextureImporter newImporter = AssetImporter.GetAtPath(newPath) as TextureImporter;
349
- if (newImporter == null)
350
- {
351
- return null;
352
341
  }
342
+ );
353
343
 
354
- newImporter.textureType = importer.textureType;
355
- newImporter.spriteImportMode = importer.spriteImportMode;
356
- newImporter.filterMode = importer.filterMode;
357
- newImporter.textureCompression = importer.textureCompression;
358
- newImporter.wrapMode = importer.wrapMode;
359
- newImporter.mipmapEnabled = importer.mipmapEnabled;
360
- newImporter.spritePixelsPerUnit = importer.spritePixelsPerUnit;
361
-
362
- TextureImporterSettings newSettings = new();
363
- newImporter.ReadTextureSettings(newSettings);
364
- newSettings.spriteExtrude = originalSettings.spriteExtrude;
365
-
366
- Vector2 origPivot = GetSpritePivot(importer);
367
- Vector2 origCenter = new(width * origPivot.x, height * origPivot.y);
368
- Vector2 newPivotPixels = origCenter - new Vector2(minX, minY);
369
- Vector2 newPivotNorm = new(
370
- cropWidth > 0 ? newPivotPixels.x / cropWidth : 0.5f,
371
- cropHeight > 0 ? newPivotPixels.y / cropHeight : 0.5f
372
- );
373
-
374
- newImporter.spriteImportMode = SpriteImportMode.Single;
375
- newImporter.spritePivot = newPivotNorm;
376
- newSettings.spritePivot = newPivotNorm;
377
- newSettings.spriteAlignment = (int)SpriteAlignment.Custom;
344
+ if (!hasVisible)
345
+ {
346
+ return null;
347
+ }
378
348
 
379
- newImporter.SetTextureSettings(newSettings);
380
- newImporter.isReadable = false;
349
+ int cropWidth = maxX - minX + 1;
350
+ int cropHeight = maxY - minY + 1;
381
351
 
382
- resultImporter = newImporter;
383
- }
384
- finally
352
+ if (_onlyNecessary && cropWidth == width && cropHeight == height)
385
353
  {
386
- if (importer != null && importer.isReadable != originalReadableState)
354
+ return null;
355
+ }
356
+
357
+ Texture2D cropped = new(cropWidth, cropHeight, TextureFormat.RGBA32, false);
358
+ Color32[] croppedPixels = new Color32[cropWidth * cropHeight];
359
+
360
+ Parallel.For(
361
+ 0,
362
+ cropHeight,
363
+ y =>
387
364
  {
388
- importer.isReadable = originalReadableState;
389
- importer.SaveAndReimport();
365
+ int sourceYOffset = (y + minY) * width;
366
+ int destYOffset = y * cropWidth;
367
+ for (int x = 0; x < cropWidth; ++x)
368
+ {
369
+ croppedPixels[destYOffset + x] = pixels[sourceYOffset + x + minX];
370
+ }
390
371
  }
372
+ );
373
+
374
+ cropped.SetPixels32(croppedPixels);
375
+ cropped.Apply();
376
+
377
+ string newPath = Path.Combine(
378
+ assetDirectory,
379
+ CroppedPrefix + Path.GetFileName(assetPath)
380
+ );
381
+ File.WriteAllBytes(newPath, cropped.EncodeToPNG());
382
+ DestroyImmediate(cropped);
383
+ AssetDatabase.ImportAsset(newPath);
384
+ TextureImporter newImporter = AssetImporter.GetAtPath(newPath) as TextureImporter;
385
+ if (newImporter == null)
386
+ {
387
+ return null;
391
388
  }
392
389
 
390
+ newImporter.textureType = importer.textureType;
391
+ newImporter.spriteImportMode = importer.spriteImportMode;
392
+ newImporter.filterMode = importer.filterMode;
393
+ newImporter.textureCompression = importer.textureCompression;
394
+ newImporter.wrapMode = importer.wrapMode;
395
+ newImporter.mipmapEnabled = importer.mipmapEnabled;
396
+ newImporter.spritePixelsPerUnit = importer.spritePixelsPerUnit;
397
+
398
+ TextureImporterSettings newSettings = new();
399
+ importer.ReadTextureSettings(newSettings);
400
+
401
+ Vector2 origPivot = GetSpritePivot(importer);
402
+ Vector2 origCenter = new(width * origPivot.x, height * origPivot.y);
403
+ Vector2 newPivotPixels = origCenter - new Vector2(minX, minY);
404
+ Vector2 newPivotNorm = new(
405
+ cropWidth > 0 ? newPivotPixels.x / cropWidth : 0.5f,
406
+ cropHeight > 0 ? newPivotPixels.y / cropHeight : 0.5f
407
+ );
408
+
409
+ newImporter.spriteImportMode = SpriteImportMode.Single;
410
+ newImporter.spritePivot = newPivotNorm;
411
+ newSettings.spritePivot = newPivotNorm;
412
+ newSettings.spriteAlignment = (int)SpriteAlignment.Custom;
413
+
414
+ newImporter.SetTextureSettings(newSettings);
415
+ newImporter.isReadable = true;
416
+ newImporter.SaveAndReimport();
417
+
418
+ TextureImporter resultImporter = newImporter;
393
419
  return resultImporter;
394
420
  }
395
421