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