ImagePacker.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. //#define PACKING_DEBUG
  2. using System;
  3. using UnityEngine;
  4. using Unity.Collections;
  5. using Unity.Collections.LowLevel.Unsafe;
  6. namespace UnityEditor.U2D.Common
  7. {
  8. internal static class ImagePacker
  9. {
  10. /// <summary>
  11. /// Given an array of rects, the method returns an array of rects arranged within outPackedWidth and outPackedHeight
  12. /// </summary>
  13. /// <param name="rects">Rects to pack</param>
  14. /// <param name="padding">Padding between each rect</param>
  15. /// <param name="outPackedRects">Rects arranged within outPackedWidth and outPackedHeight</param>
  16. /// <param name="outPackedWidth">Width of the packed rects</param>
  17. /// <param name="outPackedHeight">Height of the packed rects</param>
  18. public static void Pack(RectInt[] rects, int padding, out RectInt[] outPackedRects, out int outPackedWidth, out int outPackedHeight)
  19. {
  20. var packNode = InternalPack(rects, padding);
  21. outPackedWidth = packNode.rect.width;
  22. outPackedHeight = packNode.rect.height;
  23. var visitor = new CollectPackNodePositionVisitor();
  24. packNode.AcceptVisitor(visitor);
  25. outPackedRects = new RectInt[rects.Length];
  26. for (int i = 0; i < rects.Length; ++i)
  27. outPackedRects[i] = new RectInt(visitor.positions[i].x + padding, visitor.positions[i].y + padding, rects[i].width, rects[i].height);
  28. #if PACKING_DEBUG
  29. var emptyNodeCollector = new CollectEmptyNodePositionVisitor();
  30. packNode.AcceptVisitor(emptyNodeCollector);
  31. Array.Resize(ref outPackedRects, rects.Length + emptyNodeCollector.emptyAreas.Count);
  32. for (int i = rects.Length; i < outPackedRects.Length; ++i)
  33. outPackedRects[i] = emptyNodeCollector.emptyAreas[i - rects.Length];
  34. #endif
  35. }
  36. /// <summary>
  37. /// Packs image buffer into a single buffer. Image buffers are assumed to be 4 bytes per pixel in RGBA format
  38. /// </summary>
  39. /// <param name="buffers">Image buffers to pack</param>
  40. /// <param name="width">Image buffers width</param>
  41. /// <param name="height">Image buffers height</param>
  42. /// <param name="padding">Padding between each packed image</param>
  43. /// <param name="outPackedBuffer">Packed image buffer</param>
  44. /// <param name="outPackedBufferWidth">Packed image buffer's width</param>
  45. /// <param name="outPackedBufferHeight">Packed iamge buffer's height</param>
  46. /// <param name="outPackedRect">Location of each image buffers in the packed buffer</param>
  47. /// <param name="outUVTransform">Translation data from image original buffer to packed buffer</param>
  48. public static void Pack(NativeArray<Color32>[] buffers, int width, int height, int padding, out NativeArray<Color32> outPackedBuffer, out int outPackedBufferWidth, out int outPackedBufferHeight, out RectInt[] outPackedRect, out Vector2Int[] outUVTransform)
  49. {
  50. UnityEngine.Profiling.Profiler.BeginSample("Pack");
  51. // Determine the area that contains data in the buffer
  52. outPackedBuffer = default(NativeArray<Color32>);
  53. try
  54. {
  55. var tightRects = FindTightRectJob.Execute(buffers, width, height);
  56. Pack(tightRects, padding, out outPackedRect, out outPackedBufferWidth, out outPackedBufferHeight);
  57. outUVTransform = new Vector2Int[tightRects.Length];
  58. for (int i = 0; i < outUVTransform.Length; ++i)
  59. {
  60. outUVTransform[i] = new Vector2Int(outPackedRect[i].x - tightRects[i].x, outPackedRect[i].y - tightRects[i].y);
  61. }
  62. outPackedBuffer = new NativeArray<Color32>(outPackedBufferWidth * outPackedBufferHeight, Allocator.Persistent);
  63. Blit(outPackedBuffer, outPackedRect, outPackedBufferWidth, buffers, tightRects, width, padding);
  64. }
  65. catch (Exception ex)
  66. {
  67. if (outPackedBuffer.IsCreated)
  68. outPackedBuffer.Dispose();
  69. throw ex;
  70. }
  71. finally
  72. {
  73. UnityEngine.Profiling.Profiler.EndSample();
  74. }
  75. }
  76. static ImagePackNode InternalPack(RectInt[] rects, int padding)
  77. {
  78. if (rects == null || rects.Length == 0)
  79. return new ImagePackNode() { rect = new RectInt(0, 0, 0, 0)};
  80. var sortedRects = new ImagePackRect[rects.Length];
  81. for (int i = 0; i < rects.Length; ++i)
  82. {
  83. sortedRects[i] = new ImagePackRect();
  84. sortedRects[i].rect = rects[i];
  85. sortedRects[i].index = i;
  86. }
  87. Array.Sort<ImagePackRect>(sortedRects);
  88. var root = new ImagePackNode();
  89. root.rect = new RectInt(0, 0, (int)NextPowerOfTwo((ulong)rects[0].width), (int)NextPowerOfTwo((ulong)rects[0].height));
  90. for (int i = 0; i < rects.Length; ++i)
  91. {
  92. if (!root.Insert(sortedRects[i], padding)) // we can't fit
  93. {
  94. int newWidth = root.rect.width , newHeight = root.rect.height;
  95. if (root.rect.width < root.rect.height)
  96. newWidth = (int)NextPowerOfTwo((ulong)root.rect.width + 1);
  97. else
  98. newHeight = (int)NextPowerOfTwo((ulong)root.rect.height + 1);
  99. // Reset all packing and try again
  100. root = new ImagePackNode();
  101. root.rect = new RectInt(0, 0, newWidth, newHeight);
  102. i = -1;
  103. }
  104. }
  105. return root;
  106. }
  107. public static unsafe void Blit(NativeArray<Color32> buffer, RectInt[] blitToArea, int bufferbytesPerRow, NativeArray<Color32>[] originalBuffer, RectInt[] blitFromArea, int bytesPerRow, int padding)
  108. {
  109. UnityEngine.Profiling.Profiler.BeginSample("Blit");
  110. var c = (Color32*)buffer.GetUnsafePtr();
  111. for (int bufferIndex = 0; bufferIndex < blitToArea.Length && bufferIndex < originalBuffer.Length && bufferIndex < blitFromArea.Length; ++bufferIndex)
  112. {
  113. var b = (Color32*)originalBuffer[bufferIndex].GetUnsafeReadOnlyPtr();
  114. var rectFrom = blitFromArea[bufferIndex];
  115. var rectTo = blitToArea[bufferIndex];
  116. for (int i = 0; i < rectFrom.height; ++i)
  117. {
  118. for (int j = 0; j < rectFrom.width; ++j)
  119. {
  120. Color32 cc = b[(rectFrom.y + i) * bytesPerRow + rectFrom.x + j];
  121. c[((rectTo.y + i) * bufferbytesPerRow) + rectTo.x + j] = cc;
  122. }
  123. }
  124. }
  125. #if PACKING_DEBUG
  126. var emptyColors = new Color32[]
  127. {
  128. new Color32((byte)255, (byte)0, (byte)0, (byte)255),
  129. new Color32((byte)255, (byte)255, (byte)0, (byte)255),
  130. new Color32((byte)255, (byte)0, (byte)255, (byte)255),
  131. new Color32((byte)255, (byte)255, (byte)255, (byte)255),
  132. new Color32((byte)0, (byte)255, (byte)0, (byte)255),
  133. new Color32((byte)0, (byte)0, (byte)255, (byte)255)
  134. };
  135. for (int k = originalBuffer.Length; k < blitToArea.Length; ++k)
  136. {
  137. var rectFrom = blitToArea[k];
  138. for (int i = 0; i < rectFrom.height; ++i)
  139. {
  140. for (int j = 0; j < rectFrom.width; ++j)
  141. {
  142. c[((rectFrom.y + i) * bufferbytesPerRow) + rectFrom.x + j] =
  143. emptyColors[k % emptyColors.Length];
  144. }
  145. }
  146. }
  147. #endif
  148. UnityEngine.Profiling.Profiler.EndSample();
  149. }
  150. internal static ulong NextPowerOfTwo(ulong v)
  151. {
  152. v -= 1;
  153. v |= v >> 16;
  154. v |= v >> 8;
  155. v |= v >> 4;
  156. v |= v >> 2;
  157. v |= v >> 1;
  158. return v + 1;
  159. }
  160. internal class ImagePackRect : IComparable<ImagePackRect>
  161. {
  162. public RectInt rect;
  163. public int index;
  164. public int CompareTo(ImagePackRect obj)
  165. {
  166. var lhsArea = rect.width * rect.height;
  167. var rhsArea = obj.rect.width * obj.rect.height;
  168. if (lhsArea > rhsArea)
  169. return -1;
  170. if (lhsArea < rhsArea)
  171. return 1;
  172. if (index < obj.index)
  173. return -1;
  174. return 1;
  175. }
  176. }
  177. }
  178. }