5 using System.Collections.Generic;
6 using System.Runtime.InteropServices;
8 using SiliconStudio.Core;
9 using SiliconStudio.Core.Diagnostics;
10 using SiliconStudio.TextureConverter.Requests;
12 namespace SiliconStudio.TextureConverter.TexLibraries
17 internal class AtlasTexLibrary : ITexLibrary
19 private static Logger Log = GlobalLogger.GetLogger(
"AtlasTexLibrary");
24 public AtlasTexLibrary() { }
26 public bool CanHandleRequest(TexImage image, IRequest request)
28 if (image.GetType() != typeof(TexAtlas))
33 TexAtlas atlas = (TexAtlas)image;
37 case RequestType.AtlasCreation:
38 case RequestType.AtlasExtraction:
39 case RequestType.AtlasUpdate:
47 public void Execute(TexImage image, IRequest request)
49 if (image.GetType() != typeof(TexAtlas))
51 throw new TextureToolsException(
"The given texture must be an instance of TexAtlas.");
54 TexAtlas atlas = (TexAtlas)image;
58 case RequestType.AtlasCreation:
61 case RequestType.AtlasExtraction:
64 case RequestType.AtlasUpdate:
69 Log.Error(
"AtlasTexLibrary can't handle this request: " + request.Type);
70 throw new TextureToolsException(
"AtlasTexLibrary can't handle this request: " + request.Type);
75 public void Dispose(TexImage image)
77 Marshal.FreeHGlobal(image.Data);
81 public void Dispose() { }
83 public void StartLibrary(TexImage image) { }
84 public void EndLibrary(TexImage image) { }
86 public bool SupportBGRAOrder()
100 Log.Info(
"Creating atlas ...");
103 InitalizeAtlas(atlas, request, atlasSizeIncrement);
106 if (atlasSizeIncrement == 0) OrderTexture(request);
109 Node tree = PositionTextures(atlas, request);
114 Marshal.FreeHGlobal(atlas.Data);
115 Create(atlas, request, atlasSizeIncrement + 1);
120 CopyTexturesIntoAtlasMemory(tree, atlas);
123 CreateAtlasData(tree, atlas, request.
TextureList.Count);
135 if (request.
Name != null)
137 Log.Info(
"Extracting " + request.Name +
" from atlas ...");
139 if (!atlas.Layout.TexList.ContainsKey(request.
Name))
141 Log.Error(
"The request texture name " + request.Name +
" doesn't exist in this atlas.");
142 throw new TextureToolsException(
"The request texture name " + request.
Name +
" doesn't exist in this atlas.");
144 request.Texture.Name = request.Name;
149 Log.Info(
"Extracting textures from atlas ...");
152 foreach (KeyValuePair<string, TexAtlas.TexLayout.Position> entry
in atlas.Layout.TexList)
154 texture =
new TexImage();
155 texture.Name = entry.Key;
156 request.Textures.Add(texture);
171 if (!atlas.Layout.TexList.ContainsKey(request.
Name))
173 Log.Error(
"The given texture name " + request.Name +
" doesn't exist in this atlas.");
174 throw new TextureToolsException(
"The given texture name " + request.
Name +
" doesn't exist in this atlas.");
177 TexAtlas.TexLayout.Position position = atlas.Layout.TexList[request.Name];
181 Log.Error(
"The given texture must match the dimension of the one you want to update in the atlas.");
182 throw new TextureToolsException(
"The given texture must match the dimension of the one you want to update in the atlas.");
186 int w = position.Width;
187 int h = position.Height;
195 while (w >= 1 && h >= 1 && mipmapCount < atlas.MipmapCount);
199 int x = position.UOffset;
200 int y = position.VOffset;
201 long subImageData, atlasData;
202 int xOffset, yOffset;
203 for (
int i = 0; i < mipmapCount; ++i)
205 xOffset = (int)((Decimal)x / atlas.SubImageArray[i].Width * atlas.SubImageArray[i].RowPitch);
206 yOffset = y * atlas.SubImageArray[i].RowPitch;
207 subImageData = request.Texture.SubImageArray[i].Data.ToInt64();
208 atlasData = atlas.SubImageArray[i].Data.ToInt64();
210 for (
int j = 0; j < h; ++j)
212 Utilities.CopyMemory(
new IntPtr(atlasData + j * atlas.SubImageArray[i].RowPitch + yOffset + xOffset),
new IntPtr(subImageData + j * request.
Texture.
SubImageArray[i].
RowPitch), request.Texture.SubImageArray[i].RowPitch);
215 w = w > 1 ? w >>= 1 : w;
216 h = h > 1 ? h >>= 1 : h;
217 x = x <= 1 ? 0 : x >>= 1;
218 y = y <= 1 ? 0 : y >>= 1;
229 private void ExtractTexture(TexAtlas atlas, TexImage texture, TexAtlas.TexLayout.Position position,
int minimumMipmapSize)
231 texture.Format = atlas.Format;
233 int x,
y, w, h, rowPitch, slicePitch, mipmapCount, dataSize, offset;
242 Tools.ComputePitch(texture.Format, w, h, out rowPitch, out slicePitch);
243 dataSize += slicePitch;
249 while (w >= minimumMipmapSize && h >= minimumMipmapSize && mipmapCount < atlas.MipmapCount);
251 texture.MipmapCount = mipmapCount;
252 texture.SubImageArray =
new TexImage.SubImage[mipmapCount];
253 texture.Data = Marshal.AllocHGlobal(dataSize);
254 texture.DataSize = dataSize;
255 texture.Width = position.Width;
256 texture.Height = position.Height;
258 long atlasData, textureData;
259 int xOffset, yOffset;
260 IntPtr destPtr, srcPtr;
264 x = position.UOffset;
265 y = position.VOffset;
267 for (
int i = 0; i < mipmapCount; ++i)
269 Tools.ComputePitch(texture.Format, w, h, out rowPitch, out slicePitch);
271 texture.SubImageArray[i] =
new TexImage.SubImage();
272 texture.SubImageArray[i].Data =
new IntPtr(texture.Data.ToInt64() + offset);
273 texture.SubImageArray[i].DataSize = slicePitch;
274 texture.SubImageArray[i].Width = w;
275 texture.SubImageArray[i].Height = h;
276 texture.SubImageArray[i].RowPitch = rowPitch;
277 texture.SubImageArray[i].SlicePitch = slicePitch;
279 atlasData = atlas.SubImageArray[i].Data.ToInt64();
280 textureData = texture.SubImageArray[i].Data.ToInt64();
281 xOffset = (int)((Decimal)x / atlas.SubImageArray[i].Width * atlas.SubImageArray[i].RowPitch);
282 yOffset = y * atlas.SubImageArray[i].RowPitch;
284 for (
int j = 0; j < h; ++j)
286 srcPtr =
new IntPtr(atlasData + j * atlas.SubImageArray[i].RowPitch + yOffset + xOffset);
287 destPtr =
new IntPtr(textureData + j * rowPitch);
288 Utilities.CopyMemory(destPtr, srcPtr, rowPitch);
291 offset += slicePitch;
293 w = w > 1 ? w >>= 1 : w;
294 h = h > 1 ? h >>= 1 : h;
295 x = x <= 1 ? 0 : x >>= 1;
296 y = y <= 1 ? 0 : y >>= 1;
300 texture.RowPitch = texture.SubImageArray[0].RowPitch;
301 texture.SlicePitch = texture.SubImageArray[0].SlicePitch;
303 texture.DisposingLibrary =
this;
312 private void InitalizeAtlas(TexAtlas atlas,
AtlasCreationRequest request,
int atlasSizeIncrement)
316 bool hasMipMap =
false;
319 pixelCount += texture.Width * texture.Height;
320 if (texture.MipmapCount > 1) hasMipMap =
true;
324 int alpha = (int)Math.Log(pixelCount, 2) + 1 + atlasSizeIncrement;
325 atlas.Width = (int)Math.Pow(2, alpha / 2);
326 atlas.Height = (int)Math.Pow(2, alpha - alpha / 2);
331 int size = Math.Max(atlas.Width, atlas.Height);
337 int rowPitch, slicePitch;
338 Tools.ComputePitch(atlas.Format, atlas.Width, atlas.Height, out rowPitch, out slicePitch);
339 atlas.RowPitch = rowPitch;
340 atlas.SlicePitch = slicePitch;
348 List<TexImage.SubImage> subImages =
new List<TexImage.SubImage>();
353 while (w != 1 || h != 1)
355 Tools.ComputePitch(atlas.Format, w, h, out rowPitch, out slicePitch);
356 subImages.Add(
new TexImage.SubImage()
359 DataSize = slicePitch,
363 SlicePitch = slicePitch,
366 dataSize += slicePitch;
369 w = w > 1 ? w >>= 1 : w;
370 h = h > 1 ? h >>= 1 : h;
373 atlas.DataSize = dataSize;
374 atlas.Data = Marshal.AllocHGlobal(atlas.DataSize);
376 atlas.SubImageArray = subImages.ToArray();
379 for (
int i = 0; i < atlas.SubImageArray.Length; ++i)
381 atlas.SubImageArray[i].Data =
new IntPtr(atlas.Data.ToInt64() + offset);
382 offset += atlas.SubImageArray[i].DataSize;
384 atlas.MipmapCount = mipmapCount;
388 atlas.DataSize = atlas.SlicePitch;
389 atlas.Data = Marshal.AllocHGlobal(atlas.DataSize);
391 atlas.SubImageArray[0].Data = atlas.Data;
392 atlas.SubImageArray[0].DataSize = atlas.DataSize;
393 atlas.SubImageArray[0].Width = atlas.Width;
394 atlas.SubImageArray[0].Height = atlas.Height;
395 atlas.SubImageArray[0].RowPitch = rowPitch;
396 atlas.SubImageArray[0].SlicePitch = slicePitch;
399 atlas.DisposingLibrary =
this;
419 private void QuickSort(List<TexImage> list,
int left,
int right)
423 double pivotValue = ((left + right) / 2);
424 int x = list[(int)pivotValue].DataSize;
428 while (list[i].DataSize > x)
432 while (x > list[j].DataSize)
445 QuickSort(list, left, j);
449 QuickSort(list, i, right);
462 Node root =
new Node(0, 0, atlas.Width, atlas.Height);
466 if (!
Insert(root, texture))
482 private bool Insert(Node node, TexImage tex)
484 if (node.IsEmpty() && node.IsLeaf())
486 if (node.Width < tex.Width || node.Height < tex.Height)
490 else if (node.Width == tex.Width && node.Height == tex.Height)
492 node.Texture = (TexImage)tex.Clone(
false);
496 if (node.Width - tex.Width >= node.Height - tex.Height)
498 node.Left =
new Node(node.X, node.Y, tex.Width, node.Height);
499 node.Right =
new Node(node.X + tex.Width, node.Y, node.Width - tex.Width, node.Height);
500 return Insert(node.Left, tex);
504 node.Left =
new Node(node.X, node.Y, node.Width, tex.Height);
505 node.Right =
new Node(node.X, node.Y + tex.Height, node.Width, node.Height - tex.Height);
506 return Insert(node.Left, tex);
509 else if (!node.IsLeaf())
511 return Insert(node.Left, tex) ||
Insert(node.Right, tex);
525 private void CopyTexturesIntoAtlasMemory(Node node, TexAtlas atlas)
527 if (!node.IsEmpty() && node.IsLeaf())
529 long atlasData = atlas.Data.ToInt64();
530 long textureData = node.Texture.Data.ToInt64();
535 int x,
y, xOffset, yOffset;
536 IntPtr destPtr, srcPtr;
541 for (
int i = 0; i < node.Texture.MipmapCount && i < atlas.MipmapCount; ++i)
543 atlasData = atlas.SubImageArray[i].Data.ToInt64();
544 textureData = node.Texture.SubImageArray[i].Data.ToInt64();
545 xOffset = (int)((Decimal)x / (
Decimal)atlas.SubImageArray[i].Width * atlas.SubImageArray[i].RowPitch);
546 yOffset = y * atlas.SubImageArray[i].RowPitch;
554 for (
int j = 0; j < node.Texture.SubImageArray[i].Height; ++j)
556 destPtr =
new IntPtr(atlasData + j * atlas.SubImageArray[i].RowPitch + yOffset + xOffset);
557 srcPtr =
new IntPtr(textureData + j * node.Texture.SubImageArray[i].RowPitch);
558 Utilities.CopyMemory(destPtr, srcPtr, node.Texture.SubImageArray[i].RowPitch);
561 x = x <= 1 ? 0 : x >>= 1;
562 y = y <= 1 ? 0 : y >>= 1;
565 node.Texture.Dispose();
567 else if (!node.IsLeaf())
569 CopyTexturesIntoAtlasMemory(node.Left, atlas);
570 CopyTexturesIntoAtlasMemory(node.Right, atlas);
581 private void CreateAtlasData(Node node, TexAtlas atlas,
int textureCount)
583 atlas.Layout.TexList.Clear();
584 UpdateAtlasData(node, atlas);
593 private void UpdateAtlasData(Node node, TexAtlas atlas)
595 if (!node.IsEmpty() && node.IsLeaf())
597 if (atlas.Layout.TexList.ContainsKey(node.Texture.Name) || node.Texture.Name.Equals(
"")) node.Texture.Name = node.Texture.Name +
"_x" + node.X +
"_y" + node.Y;
598 atlas.Layout.TexList.Add(node.Texture.Name,
new TexAtlas.TexLayout.Position(node.X, node.Y, node.Width, node.Height));
600 else if (!node.IsLeaf())
602 UpdateAtlasData(node.Left, atlas);
603 UpdateAtlasData(node.Right, atlas);
619 public int X,
Y, Width, Height;
621 public Node(
int x,
int y,
int width,
int height)
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ float size_t y
TexImage Texture
The new texture.
Base implementation for ILogger.
bool ForceSquaredAtlas
The boolean to decide whether the atlas will be squared.
List< TexImage > TextureList
The texture list that will populate the atlas.
Creates a new file, always. If a file exists, the function overwrites the file, clears the existing a...
Same as Deferred mode, except sprites are sorted by texture prior to drawing. This can improve perfor...
string Name
The name of the texture to replace in the atlas.
Request to create an atlas from a texture list.
_In_ size_t _In_ size_t size
Output message to log right away.
Request to update a texture from an atlas