No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SpriteAtlasScriptablePacker.cs 35KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861
  1. using System.IO;
  2. using UnityEngine;
  3. using UnityEditor.U2D;
  4. using Unity.Collections;
  5. using Unity.Mathematics;
  6. using Unity.Jobs;
  7. using Unity.Burst;
  8. using Unity.Collections.LowLevel.Unsafe;
  9. namespace UnityEditor.U2D.Common.SpriteAtlasPacker
  10. {
  11. // Pixel Mask. Stores Rasterized Sprite Pixels.
  12. internal struct PixelMask
  13. {
  14. // Actual Size
  15. internal int2 size;
  16. // Border to minize search criteria.
  17. internal int4 rect;
  18. // Intermediate MinMax.
  19. internal int4 minmax;
  20. // Input Rect.
  21. internal int4 texrect;
  22. // Rasterized Texture Data.
  23. [NativeDisableContainerSafetyRestriction]
  24. internal NativeArray<byte> pixels;
  25. };
  26. // Atlas Masks. Stores Multiple Rasterized Sprite Pixels.
  27. internal struct AtlasMask
  28. {
  29. // Actual Size
  30. internal int2 size;
  31. // Border to minize search criteria.
  32. internal int4 rect;
  33. // Intermediate MinMax.
  34. internal int4 minmax;
  35. // Rasterized Texture Data.
  36. [NativeDisableContainerSafetyRestriction]
  37. internal NativeArray<byte> pixels;
  38. };
  39. // Internal Config params.
  40. internal struct UPackConfig
  41. {
  42. // Padding
  43. internal int padding;
  44. // Is Tight Packing. 1 for TIght.
  45. internal int packing;
  46. // Enable Rotation.
  47. internal int rotates;
  48. // Max Texture Size.
  49. internal int maxSize;
  50. // Block Offset.
  51. internal int bOffset;
  52. // Reserved.
  53. internal int freeBox;
  54. // Reserved.
  55. internal int jobSize;
  56. // Reserved.
  57. internal int sprSize;
  58. }
  59. [BurstCompile]
  60. internal struct UPack
  61. {
  62. ////////////////////////////////////////////////////////////////
  63. // Pixel Fetch.
  64. ////////////////////////////////////////////////////////////////
  65. internal static unsafe Color32* GetPixelOffsetBuffer(int offset, Color32* pixels)
  66. {
  67. return pixels + offset;
  68. }
  69. internal static unsafe Color32 GetPixel(Color32* pixels, ref int2 textureCfg, int x, int y)
  70. {
  71. int offset = x + (y * textureCfg.x);
  72. return *(pixels + offset);
  73. }
  74. internal static float Min3(float a, float b, float c)
  75. {
  76. var bc = math.min(b, c);
  77. return math.min(a, bc);
  78. }
  79. internal static byte Color32ToByte(Color32 rgba)
  80. {
  81. var r = (int)(rgba.r / 32);
  82. var g = (int)(rgba.g / 64);
  83. var b = (int)(rgba.b / 32);
  84. return (byte)(r | (g << 3) | (b << 5));
  85. }
  86. internal static Color32 ByteToColor32(byte rgb)
  87. {
  88. Color32 c = new Color32();
  89. int rgba = (int)rgb;
  90. c.r = (byte)((rgba & 0x00000007) * 32);
  91. c.g = (byte)(((rgba >> 3) & 0x00000003) * 64);
  92. c.b = (byte)(((rgba >> 5) & 0x00000007) * 32);
  93. c.a = ((int)c.r != 0 || (int)c.g != 0 || (int)c.b != 0) ? (byte)255 : (byte)0;
  94. return c;
  95. }
  96. ////////////////////////////////////////////////////////////////
  97. // Rasterization.
  98. ////////////////////////////////////////////////////////////////
  99. internal static float Max3(float a, float b, float c)
  100. {
  101. var bc = math.max(b, c);
  102. return math.max(a, bc);
  103. }
  104. internal static int Orient2d(float2 a, float2 b, float2 c)
  105. {
  106. return (int)((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x));
  107. }
  108. [BurstCompile]
  109. internal static unsafe void Pixelate(ref PixelMask pixelMask, ref int2 textureCfg, Color32* pixels, ref Color32 target, byte targetColor, int sx, int sy, int x, int y)
  110. {
  111. int _x = x - pixelMask.texrect.x;
  112. int _y = y - pixelMask.texrect.y;
  113. #if DEBUGIMAGE
  114. Color32 src = GetPixel(pixels, ref textureCfg, sx, sy); // To debug with real colors.
  115. src = ( src.a != 0 ) ? src : target;
  116. pixelMask.pixels[(_y * pixelMask.size.x) + _x] = Color32ToByte(src);
  117. #else
  118. pixelMask.pixels[(_y * pixelMask.size.x) + _x] = targetColor;
  119. #endif
  120. pixelMask.minmax.x = math.min(_x, pixelMask.minmax.x);
  121. pixelMask.minmax.y = math.min(_y, pixelMask.minmax.y);
  122. pixelMask.minmax.z = math.max(_x, pixelMask.minmax.z);
  123. pixelMask.minmax.w = math.max(_y, pixelMask.minmax.w);
  124. }
  125. [BurstCompile]
  126. internal static unsafe void Pad(ref PixelMask pixelMask, ref Color32 tgtColor, byte tgtColorByte, int dx, int dy, int padx, int pady)
  127. {
  128. for (int y = -pady; y < pady; ++y)
  129. {
  130. for (int x = -padx; x < padx; ++x)
  131. {
  132. int _x = math.min(math.max(dx + x, 0), pixelMask.size.x) - pixelMask.texrect.x;
  133. int _y = math.min(math.max(dy + y, 0), pixelMask.size.y) - pixelMask.texrect.y;
  134. if (_x < 0 || _y < 0 || _x > pixelMask.size.x || _y > pixelMask.size.y)
  135. continue;
  136. if (pixelMask.pixels[(_y * pixelMask.size.x) + _x] == 0)
  137. {
  138. #if DEBUGIMAGE
  139. pixelMask.pixels[(_y * pixelMask.size.x) + _x] = Color32ToByte(tgtColor);
  140. #else
  141. pixelMask.pixels[(_y * pixelMask.size.x) + _x] = tgtColorByte;
  142. #endif
  143. pixelMask.minmax.x = math.min(_x, pixelMask.minmax.x);
  144. pixelMask.minmax.y = math.min(_y, pixelMask.minmax.y);
  145. pixelMask.minmax.z = math.max(_x, pixelMask.minmax.z);
  146. pixelMask.minmax.w = math.max(_y, pixelMask.minmax.w);
  147. }
  148. }
  149. }
  150. }
  151. [BurstCompile]
  152. internal static unsafe void RasterizeTriangle(ref UPackConfig cfg, ref PixelMask pixelMask, Color32* pixels, ref int2 textureCfg, ref Color32 srcColor, byte srcColorByte, ref float2 v0, ref float2 v1, ref float2 v2, int padx, int pady)
  153. {
  154. // Compute triangle bounding box
  155. int minX = (int)Min3(v0.x, v1.x, v2.x);
  156. int minY = (int)Min3(v0.y, v1.y, v2.y);
  157. int maxX = (int)Max3(v0.x, v1.x, v2.x);
  158. int maxY = (int)Max3(v0.y, v1.y, v2.y);
  159. var padColor = new Color32(64, 254, 64, 254);
  160. var padColorByte = Color32ToByte(padColor);
  161. // Clip against bounds
  162. minX = math.max(minX, 0);
  163. minY = math.max(minY, 0);
  164. maxX = math.min(maxX, pixelMask.rect.x - 1);
  165. maxY = math.min(maxY, pixelMask.rect.y - 1);
  166. // Triangle setup
  167. int A01 = (int)(v0.y - v1.y), B01 = (int)(v1.x - v0.x);
  168. int A12 = (int)(v1.y - v2.y), B12 = (int)(v2.x - v1.x);
  169. int A20 = (int)(v2.y - v0.y), B20 = (int)(v0.x - v2.x);
  170. // Barycentric coordinates at minX/minY corner
  171. float2 p = new float2(minX, minY);
  172. int w0_row = Orient2d(v1, v2, p);
  173. int w1_row = Orient2d(v2, v0, p);
  174. int w2_row = Orient2d(v0, v1, p);
  175. // Rasterize
  176. for (int py = minY; py <= maxY; ++py)
  177. {
  178. // Barycentric coordinates at start of row
  179. int w0 = w0_row;
  180. int w1 = w1_row;
  181. int w2 = w2_row;
  182. for (int px = minX; px <= maxX; ++px)
  183. {
  184. // If p is on or inside all edges, render pixel.
  185. if ((w0 | w1 | w2) >= 0)
  186. {
  187. int _padx = px + padx;
  188. int _pady = py + pady;
  189. Pixelate(ref pixelMask, ref textureCfg, pixels, ref srcColor, srcColorByte, px, py, _padx, _pady);
  190. Pad(ref pixelMask, ref padColor, padColorByte, _padx, _pady, padx, pady);
  191. }
  192. // One step to the right
  193. w0 += A12;
  194. w1 += A20;
  195. w2 += A01;
  196. }
  197. // One row step
  198. w0_row += B12;
  199. w1_row += B20;
  200. w2_row += B01;
  201. }
  202. }
  203. [BurstCompile]
  204. internal static unsafe bool Rasterize(ref UPackConfig cfg, Color32* pixels, ref int2 textureCfg, Vector2* vertices, int vertexCount, int* indices, int indexCount, ref PixelMask pixelMask, int padx, int pady)
  205. {
  206. var _v = float2.zero;
  207. var srcColor = new Color32(64, 64, 254, 254);
  208. var srcColorByte = Color32ToByte(srcColor);
  209. for (int i = 0; i < indexCount; i = i + 3)
  210. {
  211. int i1 = indices[i + 0];
  212. int i2 = indices[i + 1];
  213. int i3 = indices[i + 2];
  214. float2 v1 = vertices[i1];
  215. float2 v2 = vertices[i2];
  216. float2 v3 = vertices[i3];
  217. if (Orient2d(v1, v2, v3) < 0)
  218. {
  219. _v = v1;
  220. v1 = v2;
  221. v2 = _v;
  222. }
  223. RasterizeTriangle(ref cfg, ref pixelMask, pixels, ref textureCfg, ref srcColor, srcColorByte, ref v1, ref v2, ref v3, padx, pady);
  224. }
  225. return true;
  226. }
  227. ////////////////////////////////////////////////////////////////
  228. // Rasterization.
  229. ////////////////////////////////////////////////////////////////
  230. [BurstCompile]
  231. internal unsafe struct SpriteRaster : IJob
  232. {
  233. // Pack Config
  234. public UPackConfig cfg;
  235. // Index to process.
  236. public int index;
  237. // Texture Input
  238. public int2 textureCfg;
  239. // Input Pixels
  240. [NativeDisableUnsafePtrRestriction]
  241. public Color32* pixels;
  242. // Vector2 positions.
  243. [NativeDisableUnsafePtrRestriction]
  244. public Vector2* vertices;
  245. // Vertex Count
  246. public int vertexCount;
  247. // Indices
  248. [NativeDisableUnsafePtrRestriction]
  249. public int* indices;
  250. // Index Count;
  251. public int indexCount;
  252. // SpriteRaster
  253. [NativeDisableContainerSafetyRestriction]
  254. public NativeArray<PixelMask> spriteMasks;
  255. public void Execute()
  256. {
  257. // Rasterize Source Sprite.
  258. var spriteMask = spriteMasks[index];
  259. spriteMask.rect.z = spriteMask.rect.w = spriteMask.minmax.z = spriteMask.minmax.w = 0;
  260. spriteMask.rect.x = spriteMask.rect.y = spriteMask.minmax.x = spriteMask.minmax.y = cfg.sprSize;
  261. UPack.Rasterize(ref cfg, pixels, ref textureCfg, vertices, vertexCount, indices, indexCount, ref spriteMask, cfg.padding, cfg.padding);
  262. spriteMask.rect.x = math.max(0, spriteMask.minmax.x - cfg.padding);
  263. spriteMask.rect.y = math.max(0, spriteMask.minmax.y - cfg.padding);
  264. spriteMask.rect.z = math.min(cfg.maxSize, spriteMask.minmax.z + cfg.padding);
  265. spriteMask.rect.w = math.min(cfg.maxSize, spriteMask.minmax.w + cfg.padding);
  266. byte color = Color32ToByte(new Color32(254, 64, 64, 254));
  267. // If Tight packing fill Rect.
  268. if (0 == cfg.packing)
  269. {
  270. for (int y = spriteMask.rect.y; y <= spriteMask.rect.w; ++y)
  271. {
  272. for (int x = spriteMask.rect.x; x <= spriteMask.rect.z; ++x)
  273. {
  274. spriteMask.pixels[y * spriteMask.size.x + x] = (spriteMask.pixels[y * spriteMask.size.x + x] != 0) ? spriteMask.pixels[y * spriteMask.size.x + x] : color;
  275. }
  276. }
  277. }
  278. spriteMasks[index] = spriteMask;
  279. }
  280. }
  281. ////////////////////////////////////////////////////////////////
  282. // Atlas Packing.
  283. ////////////////////////////////////////////////////////////////
  284. [BurstCompile]
  285. internal static bool TestMask(ref AtlasMask atlasMask, ref PixelMask spriteMask, int ax, int ay, int sx, int sy)
  286. {
  287. var satlasPixel = atlasMask.pixels[ay * atlasMask.size.x + ax];
  288. var spritePixel = spriteMask.pixels[sy * spriteMask.size.x + sx];
  289. return (spritePixel > 0 && satlasPixel > 0);
  290. }
  291. [BurstCompile]
  292. internal static unsafe bool TestMask(ref AtlasMask atlasMask, ref PixelMask spriteMask, int x, int y)
  293. {
  294. var spriteRect = spriteMask.rect;
  295. if (TestMask(ref atlasMask, ref spriteMask, (x), (y), spriteRect.x, spriteRect.y))
  296. return false;
  297. if (TestMask(ref atlasMask, ref spriteMask, (x), (y + (spriteRect.w - spriteRect.y)), spriteRect.x, spriteRect.y))
  298. return false;
  299. if (TestMask(ref atlasMask, ref spriteMask, (x + (spriteRect.z - spriteRect.x)), (y), spriteRect.z, spriteRect.w))
  300. return false;
  301. if (TestMask(ref atlasMask, ref spriteMask, (x + (spriteRect.z - spriteRect.x)), (y + (spriteRect.w - spriteRect.y)), spriteRect.z, spriteRect.w))
  302. return false;
  303. if (TestMask(ref atlasMask, ref spriteMask, (x), (y), spriteRect.z / 2, spriteRect.y / 2))
  304. return false;
  305. for (int j = spriteRect.y, _j = 0; j < spriteRect.w; ++j, ++_j)
  306. {
  307. for (int i = spriteRect.x, _i = 0; i < spriteRect.z; ++i, ++_i)
  308. {
  309. if (TestMask(ref atlasMask, ref spriteMask, (_i + x), (_j + y), i, j))
  310. return false;
  311. }
  312. }
  313. return true;
  314. }
  315. [BurstCompile]
  316. internal static void ApplyMask(ref UPackConfig cfg, ref AtlasMask atlasMask, ref PixelMask spriteMask, int ax, int ay, int sx, int sy)
  317. {
  318. var pixel = spriteMask.pixels[sy * spriteMask.size.x + sx];
  319. if (pixel != 0)
  320. {
  321. atlasMask.pixels[ay * atlasMask.size.x + ax] = pixel;
  322. atlasMask.minmax.x = math.min(ax, atlasMask.minmax.x);
  323. atlasMask.minmax.y = math.min(ay, atlasMask.minmax.y);
  324. atlasMask.minmax.z = math.max(ax, atlasMask.minmax.z);
  325. atlasMask.minmax.w = math.max(ay, atlasMask.minmax.w);
  326. }
  327. }
  328. [BurstCompile]
  329. internal static unsafe void ApplyMask(ref UPackConfig cfg, ref AtlasMask atlasMask, ref PixelMask spriteMask, int x, int y)
  330. {
  331. var spriteRect = spriteMask.rect;
  332. for (int j = spriteRect.y, _j = 0; j < spriteRect.w; ++j, ++_j)
  333. {
  334. for (int i = spriteRect.x, _i = 0; i < spriteRect.z; ++i, ++_i)
  335. {
  336. ApplyMask(ref cfg, ref atlasMask, ref spriteMask, (_i + x), (_j + y), i, j);
  337. }
  338. }
  339. }
  340. ////////////////////////////////////////////////////////////////
  341. // Fit Sprite in a given RECT for Best Fit
  342. ////////////////////////////////////////////////////////////////
  343. [BurstCompile]
  344. internal struct SpriteFitter : IJob
  345. {
  346. // Cfg
  347. public UPackConfig config;
  348. // Test Inc
  349. public int4 atlasXInc;
  350. // Test Inc.
  351. public int4 atlasYInc;
  352. // Result Index.
  353. public int resultIndex;
  354. // AtlasMask
  355. public AtlasMask atlasMask;
  356. // SpriteMask
  357. public PixelMask spriteMask;
  358. // ResultSet
  359. [NativeDisableContainerSafetyRestriction]
  360. public NativeArray<int4> resultSet;
  361. public void Execute()
  362. {
  363. bool more = true;
  364. for (int y = atlasYInc.x; (more && y <= atlasYInc.y); y += atlasYInc.z)
  365. {
  366. if (y + spriteMask.rect.w >= atlasMask.rect.y)
  367. break;
  368. for (int x = atlasXInc.x; (more && x <= atlasXInc.y); x += atlasXInc.z)
  369. {
  370. if (x + spriteMask.rect.z >= atlasMask.rect.x)
  371. continue;
  372. more = TestMask(ref atlasMask, ref spriteMask, x, y) == false;
  373. if (!more)
  374. resultSet[resultIndex] = new int4(x, y, more ? 0 : 1, 0);
  375. }
  376. }
  377. }
  378. }
  379. ////////////////////////////////////////////////////////////////
  380. // Random Fit Only for Testing.
  381. ////////////////////////////////////////////////////////////////
  382. internal static unsafe bool RandomFit(ref UPackConfig cfg, ref NativeArray<SpriteFitter> fitterJob, ref NativeArray<JobHandle> fitterJobHandles, ref NativeArray<int4> resultArray, ref AtlasMask atlasMask, ref PixelMask spriteMask, ref int4 output)
  383. {
  384. bool more = true;
  385. int inc = math.min(atlasMask.rect.x, atlasMask.rect.y), rx = -1, ry = -1;
  386. int jobCount = 32;
  387. for (int i = 0; i < jobCount; ++i)
  388. fitterJobHandles[i] = default(JobHandle);
  389. System.Random rnd = new System.Random();
  390. while (more && (atlasMask.rect.x <= cfg.maxSize || atlasMask.rect.y <= cfg.maxSize))
  391. {
  392. int index = 0;
  393. int xrmax = atlasMask.minmax.z;
  394. int yrmax = atlasMask.minmax.w;
  395. // Random Search.
  396. {
  397. int ix = atlasMask.minmax.z;
  398. int iy = atlasMask.minmax.w;
  399. UnsafeUtility.MemClear(resultArray.GetUnsafePtr(), resultArray.Length * sizeof(int4));
  400. fitterJob[0] = new SpriteFitter() { atlasMask = atlasMask, spriteMask = spriteMask, atlasXInc = new int4(ix, ix, 1, 0), atlasYInc = new int4(iy, iy, 1, 0), resultSet = resultArray, resultIndex = 0 };
  401. fitterJobHandles[0] = fitterJob[0].Schedule();
  402. for (int i = 1; i < jobCount; ++i)
  403. {
  404. int x = atlasMask.minmax.z - rnd.Next(0, xrmax);
  405. int y = atlasMask.minmax.w - rnd.Next(0, yrmax);
  406. fitterJob[index] = new SpriteFitter() { atlasMask = atlasMask, spriteMask = spriteMask, atlasXInc = new int4(x, x, 1, 0), atlasYInc = new int4(y, y, 1, 0), resultSet = resultArray, resultIndex = i };
  407. fitterJobHandles[index] = fitterJob[index].Schedule();
  408. index++;
  409. }
  410. JobHandle.ScheduleBatchedJobs();
  411. var jobHandle = JobHandle.CombineDependencies(fitterJobHandles);
  412. jobHandle.Complete();
  413. int area = atlasMask.size.x * atlasMask.size.y;
  414. for (int j = 0; j < index; ++j)
  415. {
  416. if (resultArray[j].z == 1 && area > (rx * ry))
  417. {
  418. more = false;
  419. area = rx * ry;
  420. rx = resultArray[j].x;
  421. ry = resultArray[j].y;
  422. }
  423. }
  424. if (false == more)
  425. {
  426. ApplyMask(ref cfg, ref atlasMask, ref spriteMask, rx, ry);
  427. break;
  428. }
  429. }
  430. if (atlasMask.rect.x >= cfg.maxSize || atlasMask.rect.y >= cfg.maxSize)
  431. break;
  432. atlasMask.rect.x = atlasMask.rect.y = math.min(cfg.maxSize, atlasMask.rect.y + inc);
  433. }
  434. output = new int4(rx, ry, 0, 0);
  435. return (rx != -1 && ry != -1);
  436. }
  437. ////////////////////////////////////////////////////////////////
  438. // Best Fit.
  439. ////////////////////////////////////////////////////////////////
  440. internal static unsafe bool BestFit(ref UPackConfig cfg, ref NativeArray<SpriteFitter> fitterJob, ref NativeArray<JobHandle> fitterJobHandles, ref NativeArray<int4> resultArray, ref AtlasMask atlasMask, ref PixelMask spriteMask, ref int4 output)
  441. {
  442. bool more = true;
  443. int inc = math.min(atlasMask.rect.x, atlasMask.rect.y), rx = -1, ry = -1;
  444. for (int i = 0; i < cfg.jobSize; ++i)
  445. fitterJobHandles[i] = default(JobHandle);
  446. while (more)
  447. {
  448. int index = 0;
  449. UnsafeUtility.MemClear(resultArray.GetUnsafePtr(), resultArray.Length * sizeof(int4));
  450. // Small Search.
  451. for (int y = 0; (y < atlasMask.rect.y); y += inc)
  452. {
  453. fitterJob[index] = new SpriteFitter() { config = cfg, atlasMask = atlasMask, spriteMask = spriteMask, atlasXInc = new int4(0, atlasMask.rect.x, atlasMask.rect.z, 0), atlasYInc = new int4(y, y + inc, atlasMask.rect.w, 0), resultSet = resultArray, resultIndex = index };
  454. fitterJobHandles[index] = fitterJob[index].Schedule();
  455. index++;
  456. }
  457. JobHandle.ScheduleBatchedJobs();
  458. var jobHandle = JobHandle.CombineDependencies(fitterJobHandles);
  459. jobHandle.Complete();
  460. int area = atlasMask.size.x * atlasMask.size.y;
  461. for (int j = 0; j < index; ++j)
  462. {
  463. if (resultArray[j].z == 1 && area > (resultArray[j].x * resultArray[j].y))
  464. {
  465. more = false;
  466. rx = resultArray[j].x;
  467. ry = resultArray[j].y;
  468. area = rx * ry;
  469. }
  470. }
  471. if (false == more)
  472. {
  473. ApplyMask(ref cfg, ref atlasMask, ref spriteMask, rx, ry);
  474. break;
  475. }
  476. if (atlasMask.rect.x >= cfg.maxSize && atlasMask.rect.y >= cfg.maxSize)
  477. {
  478. // Either successful or need another page.
  479. break;
  480. }
  481. else
  482. {
  483. #if SQUAREINCR
  484. atlasMask.rect.x = math.min(cfg.maxSize, atlasMask.rect.x * 2);
  485. atlasMask.rect.y = math.min(cfg.maxSize, atlasMask.rect.y * 2);
  486. #else
  487. // Row Expansion first.
  488. bool incY = (atlasMask.rect.y < atlasMask.rect.x);
  489. atlasMask.rect.x = incY ? atlasMask.rect.x : math.min(cfg.maxSize, atlasMask.rect.x * 2);
  490. atlasMask.rect.y = incY ? math.min(cfg.maxSize, atlasMask.rect.y * 2) : atlasMask.rect.y;
  491. #endif
  492. }
  493. }
  494. output = new int4(rx, ry, 0, 0);
  495. return (rx != -1 && ry != -1);
  496. }
  497. }
  498. internal class SpriteAtlasScriptablePacker : UnityEditor.U2D.ScriptablePacker
  499. {
  500. static void DebugImage(NativeArray<byte> image, int w, int h, string path)
  501. {
  502. #if DEBUGIMAGE
  503. var t = new Texture2D(w, h, UnityEngine.Experimental.Rendering.GraphicsFormat.R8G8B8A8_SRGB, 0);
  504. var p = new NativeArray<Color32>(image.Length, Allocator.Persistent, NativeArrayOptions.ClearMemory);
  505. for (int i = 0; i < image.Length; ++i)
  506. p[i] = UPack.ByteToColor32(image[i]);
  507. t.SetPixelData<Color32>(p, 0);
  508. byte[] _bytes = t.EncodeToPNG();
  509. System.IO.File.WriteAllBytes(path, _bytes);
  510. #endif
  511. }
  512. static unsafe bool PrepareInput(UPackConfig cfg, int2 spriteSize, PackerData input)
  513. {
  514. for (int i = 0; i < input.spriteData.Length; ++i)
  515. {
  516. Color32* pixels = (Color32*)NativeArrayUnsafeUtility.GetUnsafeReadOnlyPtr(input.colorData);
  517. var tsize = new Vector2Int(cfg.maxSize, cfg.maxSize);
  518. var inputSpriteC = input.spriteData[i];
  519. var textureDataC = input.textureData[inputSpriteC.texIndex];
  520. var spritePixels = UPack.GetPixelOffsetBuffer(textureDataC.bufferOffset, pixels);
  521. if (inputSpriteC.rect.x + inputSpriteC.rect.width > spriteSize.x || inputSpriteC.rect.y + inputSpriteC.rect.height > spriteSize.y)
  522. {
  523. return false;
  524. }
  525. if (inputSpriteC.rect.width + (2 * cfg.padding) > cfg.maxSize || inputSpriteC.rect.height + (2 * cfg.padding) > cfg.maxSize)
  526. {
  527. return false;
  528. }
  529. #if DEBUGIMAGE
  530. var outputCoordX = 0;
  531. var outputCoordY = 0;
  532. var spriteOutput = new SpriteData();
  533. spriteOutput.texIndex = i;
  534. spriteOutput.guid = inputSpriteC.guid;
  535. spriteOutput.rect = new RectInt() { x = outputCoordX, y = outputCoordY, width = inputSpriteC.rect.width, height = inputSpriteC.rect.height };
  536. spriteOutput.output.x = 0;
  537. spriteOutput.output.y = 0;
  538. var atlasTexture = new NativeArray<Color32>(tsize.x * tsize.y, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
  539. for (int y = inputSpriteC.rect.y; y < (inputSpriteC.rect.y + inputSpriteC.rect.height); ++y)
  540. {
  541. outputCoordX = 0;
  542. var textureCfg = new int2(textureDataC.width, textureDataC.height);
  543. for (int x = inputSpriteC.rect.x; x < (inputSpriteC.rect.x + inputSpriteC.rect.width); ++x)
  544. {
  545. Color32 color = UPack.GetPixel(spritePixels, ref textureCfg, x, y);
  546. int outOffset = outputCoordX + (outputCoordY * tsize.y);
  547. atlasTexture[outOffset] = color;
  548. outputCoordX++;
  549. }
  550. outputCoordY++;
  551. }
  552. {
  553. Texture2D t = new Texture2D(cfg.maxSize, cfg.maxSize, UnityEngine.Experimental.Rendering.GraphicsFormat.R8G8B8A8_SRGB, 0);
  554. t.SetPixelData<Color32>(atlasTexture, 0);
  555. byte[] _bytes = UnityEngine.ImageConversion.EncodeToPNG(t);
  556. System.IO.File.WriteAllBytes(Path.Combine(Application.dataPath, "../") + "Temp/" + "Input" + i + ".png", _bytes);
  557. }
  558. atlasTexture.Dispose();
  559. #endif
  560. }
  561. return true;
  562. }
  563. public override bool Pack(SpriteAtlasPackingSettings config, SpriteAtlasTextureSettings setting, PackerData input)
  564. {
  565. var cfg = new UPackConfig();
  566. var quality = 3;
  567. var startRect = 128;
  568. var c = new Color32(32, 64, 128, 255);
  569. var b = UPack.Color32ToByte(c);
  570. var d = UPack.ByteToColor32(b);
  571. cfg.padding = config.padding;
  572. cfg.bOffset = config.blockOffset * (1 << (int)quality);
  573. cfg.maxSize = setting.maxTextureSize;
  574. cfg.rotates = config.enableRotation ? 1 : 0;
  575. cfg.packing = config.enableTightPacking ? 1 : 0;
  576. cfg.freeBox = cfg.bOffset;
  577. cfg.jobSize = 1024;
  578. cfg.sprSize = 2048;
  579. var spriteCount = input.spriteData.Length;
  580. var spriteBatch = math.min(spriteCount, SystemInfo.processorCount);
  581. // Because Atlas Masks are Serial / Raster in Jobs.
  582. var atlasCount = 0;
  583. var spriteSize = new int2(cfg.sprSize, cfg.sprSize);
  584. var validAtlas = true;
  585. // Rasterization.
  586. NativeArray<AtlasMask> atlasMasks = new NativeArray<AtlasMask>(spriteCount, Allocator.Persistent, NativeArrayOptions.ClearMemory);
  587. NativeArray<PixelMask> spriteMasks = new NativeArray<PixelMask>(spriteBatch, Allocator.Persistent, NativeArrayOptions.ClearMemory);
  588. var rasterJobHandles = new NativeArray<JobHandle>(spriteBatch, Allocator.Persistent);
  589. var rasterJob = new NativeArray<UPack.SpriteRaster>(spriteBatch, Allocator.Persistent);
  590. // PolygonFitting
  591. var fitterJobHandles = new NativeArray<JobHandle>(cfg.jobSize, Allocator.Persistent);
  592. var fitterJob = new NativeArray<UPack.SpriteFitter>(cfg.jobSize, Allocator.Persistent);
  593. var fitterResult = new NativeArray<int4>(cfg.jobSize, Allocator.Persistent);
  594. // Initialize Batch Sprite Masks.
  595. for (int i = 0; i < spriteBatch; ++i)
  596. {
  597. PixelMask spriteMask = new PixelMask();
  598. spriteMask.pixels = new NativeArray<byte>(spriteSize.x * spriteSize.y, Allocator.Persistent, NativeArrayOptions.ClearMemory);
  599. spriteMask.size = spriteSize;
  600. spriteMask.rect = int4.zero;
  601. spriteMask.minmax = new int4(spriteSize.x, spriteSize.y, 0, 0);
  602. spriteMasks[i] = spriteMask;
  603. }
  604. unsafe
  605. {
  606. // Prepare.
  607. bool v = PrepareInput(cfg, spriteSize, input);
  608. if (!v)
  609. return false;
  610. // Copy back to Processing Data
  611. for (int batch = 0; batch < spriteCount; batch += spriteBatch)
  612. {
  613. var spriteBgn = batch;
  614. var spriteEnd = math.min(spriteCount, spriteBgn + spriteBatch);
  615. int index = 0;
  616. for (int i = spriteBgn; i < spriteEnd; ++i)
  617. {
  618. var inputSprite = input.spriteData[i];
  619. var textureData = input.textureData[inputSprite.texIndex];
  620. // Clear Mem of SpriteMask.
  621. var spriteMask = spriteMasks[index];
  622. UnsafeUtility.MemClear(spriteMask.pixels.GetUnsafePtr(), ((spriteMask.rect.w * spriteMask.size.x) + spriteMask.rect.z) * UnsafeUtility.SizeOf<Color32>());
  623. spriteMask.size = spriteSize;
  624. spriteMask.rect = int4.zero;
  625. spriteMask.minmax = new int4(spriteSize.x, spriteSize.y, 0, 0);
  626. spriteMask.texrect = new int4(inputSprite.rect.x, inputSprite.rect.y, inputSprite.rect.width, inputSprite.rect.height);
  627. spriteMasks[index] = spriteMask;
  628. unsafe
  629. {
  630. rasterJob[index] = new UPack.SpriteRaster()
  631. {
  632. cfg = cfg,
  633. index = index,
  634. pixels = (Color32*)NativeArrayUnsafeUtility.GetUnsafeReadOnlyPtr(input.colorData) + textureData.bufferOffset,
  635. vertices = (Vector2*)NativeArrayUnsafeUtility.GetUnsafeReadOnlyPtr(input.vertexData) + inputSprite.vertexOffset,
  636. vertexCount = inputSprite.vertexCount,
  637. indices = (int*)NativeArrayUnsafeUtility.GetUnsafeReadOnlyPtr(input.indexData) + inputSprite.indexOffset,
  638. indexCount = inputSprite.indexCount,
  639. textureCfg = new int2(textureData.width, textureData.height),
  640. spriteMasks = spriteMasks
  641. };
  642. }
  643. rasterJobHandles[index] = rasterJob[index].Schedule();
  644. index++;
  645. }
  646. JobHandle.ScheduleBatchedJobs();
  647. var jobHandle = JobHandle.CombineDependencies(rasterJobHandles);
  648. jobHandle.Complete();
  649. index = 0;
  650. for (int sprite = spriteBgn; sprite < spriteEnd; ++sprite)
  651. {
  652. var inputSpriteC = input.spriteData[sprite];
  653. // Rasterize Source Sprite.
  654. var spriteMask = spriteMasks[index];
  655. int page = -1;
  656. validAtlas = false;
  657. var result = int4.zero;
  658. for (int i = (atlasCount - 1); i >= 0 && false == validAtlas; --i)
  659. {
  660. var atlasMask = atlasMasks[i];
  661. validAtlas = UPack.BestFit(ref cfg, ref fitterJob, ref fitterJobHandles, ref fitterResult, ref atlasMask, ref spriteMask, ref result);
  662. if (validAtlas)
  663. {
  664. atlasMasks[i] = atlasMask;
  665. page = i;
  666. }
  667. }
  668. // Test
  669. if (!validAtlas)
  670. {
  671. page = atlasCount;
  672. AtlasMask atlasMask = new AtlasMask();
  673. atlasMask.pixels = new NativeArray<byte>(cfg.maxSize * cfg.maxSize, Allocator.Persistent, NativeArrayOptions.ClearMemory);
  674. atlasMask.size = new int2(cfg.maxSize, cfg.maxSize);
  675. atlasMask.rect.x = atlasMask.rect.y = startRect;
  676. atlasMask.rect.z = atlasMask.rect.w = cfg.bOffset;
  677. validAtlas = UPack.BestFit(ref cfg, ref fitterJob, ref fitterJobHandles, ref fitterResult, ref atlasMask, ref spriteMask, ref result);
  678. atlasMasks[atlasCount] = atlasMask;
  679. atlasCount++;
  680. }
  681. if (!validAtlas)
  682. {
  683. break;
  684. }
  685. // Clear Mem of SpriteMask.
  686. DebugImage(spriteMask.pixels, cfg.maxSize, cfg.maxSize, Path.Combine(Application.dataPath, "../") + "Temp/" + "Input" + sprite + ".png");
  687. UnsafeUtility.MemClear(spriteMask.pixels.GetUnsafePtr(), ((spriteMask.rect.w * spriteMask.size.x) + spriteMask.rect.z) * UnsafeUtility.SizeOf<Color32>());
  688. inputSpriteC.output.x = result.x;
  689. inputSpriteC.output.y = result.y;
  690. inputSpriteC.output.page = validAtlas ? page : -1;
  691. input.spriteData[sprite] = inputSpriteC;
  692. index++;
  693. }
  694. if (!validAtlas)
  695. {
  696. break;
  697. }
  698. }
  699. for (int j = 0; j < atlasCount; ++j)
  700. DebugImage(atlasMasks[j].pixels, cfg.maxSize, cfg.maxSize, Path.Combine(Application.dataPath, "../") + "Temp/" + "Packer" + j + ".png");
  701. // If there is an error fallback
  702. if (!validAtlas)
  703. {
  704. for (int i = 0; i < spriteCount; ++i)
  705. {
  706. var inputSpriteC = input.spriteData[i];
  707. inputSpriteC.output.x = inputSpriteC.output.y = 0;
  708. inputSpriteC.output.page = -1;
  709. input.spriteData[i] = inputSpriteC;
  710. }
  711. }
  712. for (int j = 0; j < spriteBatch; ++j)
  713. spriteMasks[j].pixels.Dispose();
  714. for (int j = 0; j < atlasCount; ++j)
  715. atlasMasks[j].pixels.Dispose();
  716. atlasMasks.Dispose();
  717. spriteMasks.Dispose();
  718. rasterJob.Dispose();
  719. rasterJobHandles.Dispose();
  720. fitterJob.Dispose();
  721. fitterJobHandles.Dispose();
  722. }
  723. return true;
  724. }
  725. }
  726. }