A game about forced loneliness, made by TACStudios
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}