5 using System.Collections.Generic;
6 using SiliconStudio.Core.Diagnostics;
7 using SiliconStudio.Core.Mathematics;
8 using SiliconStudio.Paradox.Graphics;
9 using SiliconStudio.TextureConverter.Requests;
10 using SiliconStudio.TextureConverter.TexLibraries;
11 using System.Runtime.CompilerServices;
13 namespace SiliconStudio.TextureConverter
30 private List<ITexLibrary> textureLibraries;
32 private static Logger Log = GlobalLogger.GetLogger(
"TextureTool");
42 textureLibraries =
new List<ITexLibrary>
44 new AtitcTexLibrary(),
47 new ParadoxTexLibrary(),
49 new ColorKeyTexLibrary(),
50 new AtlasTexLibrary(),
60 foreach (ITexLibrary library
in textureLibraries)
79 ITexLibrary library = FindLibrary(atlas, request);
82 Log.Error(
"No available library could create the atlas.");
86 atlas.Format = textureList[0].Format;
87 foreach (
TexImage texture
in textureList)
89 if (texture.
Format != atlas.Format)
91 Log.Error(
"The textures in the list must all habe the same format.");
97 ExecuteRequest(atlas, request);
112 if (!
File.Exists(layoutFile))
114 Log.Error(
"The file " + layoutFile +
" doesn't exist. Please check the file path.");
115 throw new TextureToolsException(
"The file " + layoutFile +
" doesn't exist. Please check the file path.");
137 ITexLibrary library = FindLibrary(array, request);
140 Log.Error(
"No available library could create the array.");
144 int width = textureList[0].Width;
145 int height = textureList[0].Height;
146 int depth = textureList[0].Depth;
147 array.Format = textureList[0].Format;
149 foreach (var texture
in textureList)
152 if (texture.Width != width || texture.Height != height || texture.Depth != depth || texture.Format != array.Format)
154 Log.Error(
"The textures must all have the same size and format to be in a texture array.");
155 throw new TextureToolsException(
"The textures must all have the same size and format to be in a texture array.");
159 ExecuteRequest(array, request);
182 if (textureList.Count % 6 != 0)
184 Log.Error(
"The number of texture in the texture list must be a multiple of 6.");
185 throw new TextureToolsException(
"The number of texture in the texture list must be a multiple of 6.");
188 ITexLibrary library = FindLibrary(cube, request);
191 Log.Error(
"No available library could create the cube.");
195 int width = textureList[0].Width;
196 int height = textureList[0].Height;
197 int depth = textureList[0].Depth;
198 cube.Format = textureList[0].Format;
200 foreach (var texture
in textureList)
203 if (texture.Width != width || texture.Height != height || texture.Depth != depth || texture.Format != cube.Format)
205 Log.Error(
"The textures must all have the same size and format to be in a texture cube.");
206 throw new TextureToolsException(
"The textures must all have the same size and format to be in a texture cube.");
210 ExecuteRequest(cube, request);
229 if (!
File.Exists(file))
231 Log.Error(
"The file " + file +
" doesn't exist. Please check the file path.");
235 var atlas =
new TexAtlas(layout, Load(
new LoadingRequest(file)));
237 CheckConformity(atlas, layout);
256 if (!
File.Exists(file))
258 Log.Error(
"The file " + file +
" doesn't exist. Please check the file path.");
262 if (!layoutFile.Equals(
"") && !File.Exists(layoutFile))
264 Log.Error(
"The file " + layoutFile +
" doesn't exist. Please check the file path.");
265 throw new TextureToolsException(
"The file " + layoutFile +
" doesn't exist. Please check the file path.");
269 layoutFile = Path.ChangeExtension(file, TexAtlas.TexLayout.Extension);
270 if (!
File.Exists(layoutFile))
272 Log.Error(
"Please check that the layout file is in the same directory as the atlas, with the same name and " + TexAtlas.TexLayout.Extension +
" as extension.");
273 throw new TextureToolsException(
"Please check that the layout file is in the same directory as the atlas, with the same name and ." +
TexAtlas.TexLayout.Extension +
" as extension.");
277 var layout = TexAtlas.TexLayout.Import(layoutFile);
278 var atlas =
new TexAtlas(layout, Load(
new LoadingRequest(file)));
280 CheckConformity(atlas, layout);
294 int rightestPoint = 0;
296 foreach (var entry
in layout.TexList)
298 if (entry.Value.UOffset + entry.Value.Width > rightestPoint) rightestPoint = entry.Value.UOffset + entry.Value.Width;
299 if (entry.Value.VOffset + entry.Value.Height > lowestPoint) lowestPoint = entry.Value.VOffset + entry.Value.Height;
302 if (rightestPoint > atlas.
Width || lowestPoint > atlas.
Height)
304 Log.Error(
"The layout doesn't match the given atlas file.");
305 throw new TextureToolsException(
"The layout doesn't match the given atlas file.");
321 if (!
File.Exists(file))
323 Log.Error(
"The file " + file +
" doesn't exist. Please check the file path.");
327 return Load(
new LoadingRequest(file));
339 if (image == null)
throw new ArgumentNullException(
"image");
340 return Load(
new LoadingRequest(image));
350 private TexImage Load(LoadingRequest request)
353 texImage.Name = request.FilePath == null ?
"" : Path.GetFileName(request.FilePath);
355 foreach (ITexLibrary library
in textureLibraries)
357 if (library.CanHandleRequest(texImage, request))
359 library.Execute(texImage, request);
360 texImage.CurrentLibrary = library;
365 Log.Error(
"No available library could load your texture : " + request.Type);
366 throw new TextureToolsException(
"No available library could perform the task : " + request.Type);
376 if (!Tools.IsCompressed(image.
Format))
381 ExecuteRequest(image,
new DecompressingRequest());
391 public void Save(
TexImage image, String fileName,
int minimumMipMapSize=1)
393 if (fileName == null || fileName.Equals(
""))
395 Log.Error(
"No file name entered.");
399 var request =
new ExportRequest(fileName, minimumMipMapSize);
401 if (FindLibrary(image, request) == null && Tools.IsCompressed(image.
Format))
403 Log.Warning(
"No library can export this texture with the actual compression format. We will try to decompress it first.");
407 ExecuteRequest(image, request);
418 public void Save(
TexImage image, String fileName, SiliconStudio.Paradox.Graphics.PixelFormat
format,
int minimumMipMapSize = 1)
420 if (fileName == null || fileName.Equals(
""))
422 Log.Error(
"No file name entered.");
426 if (minimumMipMapSize < 0)
428 Log.Error(
"The minimup Mipmap size can't be negative. Put 0 or 1 for a complete Mipmap chain.");
429 throw new TextureToolsException(
"The minimup Mipmap size can't be negative. Put 0 or 1 for a complete Mipmap chain.");
432 if (image.
Format !=
format && Tools.IsCompressed(
format) && !Tools.IsCompressed(image.Format))
436 ExecuteRequest(workingImage,
new ExportRequest(fileName, minimumMipMapSize));
437 workingImage.Dispose();
444 ExecuteRequest(workingImage,
new ExportRequest(fileName, minimumMipMapSize));
445 workingImage.Dispose();
449 ExecuteRequest(image,
new ExportRequest(fileName, minimumMipMapSize));
464 if (Tools.IsCompressed(image.
Format))
466 Log.Warning(
"You can't switch channels of a compressed texture. It will be decompressed first..");
470 ExecuteRequest(image,
new SwitchingBRChannelsRequest());
487 if (Tools.IsCompressed(image.
Format))
489 Log.Warning(
"You can't compress an already compressed texture. It will be decompressed first..");
493 var request =
new CompressingRequest(
format, quality);
495 ExecuteRequest(image, request);
505 if (Tools.IsCompressed(image.
Format))
507 Log.Warning(
"You can't compress an already compressed texture. It will be decompressed first..");
513 Log.Error(
"ColorKey TextureConverter is only supporting R8G8B8A8_UNorm or B8G8R8A8_UNorm while Texture Format is [{0}]", image.Format);
518 ExecuteRequest(image, request);
531 if (Tools.IsCompressed(image.
Format))
533 Log.Warning(
"You can't generate mipmaps for a compressed texture. It will be decompressed first..");
537 ExecuteRequest(image,
new MipMapsGenerationRequest(filter));
553 if (width < 1 || height < 1)
555 Log.Error(
"The new size must be an integer > 0.");
560 if (image.
Width == width && image.
Height == height)
565 if (Tools.IsCompressed(image.
Format))
567 Log.Warning(
"You can't resize a compressed texture. It will be decompressed first..");
571 ExecuteRequest(image,
new FixedRescalingRequest(width, height, filter));
588 if (widthFactor <= 0 || heightFactor <= 0)
590 Log.Error(
"The size factors must be positive floats.");
595 if (widthFactor == 1 && heightFactor ==1)
600 if (Tools.IsCompressed(image.
Format))
602 Log.Warning(
"You can't rescale a compressed texture. It will be decompressed first..");
606 ExecuteRequest(image,
new FactorRescalingRequest(widthFactor, heightFactor, filter));
620 Log.Error(
"The amplitude must be a positive float.");
624 if (Tools.IsCompressed(heightMap.
Format))
626 Log.Warning(
"You can't generate a normal map from a compressed height hmap. It will be decompressed first..");
632 ExecuteRequest(heightMap, request);
634 return request.NormalMap;
644 if (Tools.IsCompressed(image.
Format))
646 Log.Warning(
"You can't premultiply alpha on a compressed texture. It will be decompressed first..");
662 var rowPtr = alphaImage.Data;
663 for (
int i = 0; i < alphaImage.Height; i++)
665 var pByte = (byte*)rowPtr;
666 for (
int x = 0; x < alphaImage.Width; x++)
674 rowPtr = IntPtr.Add(rowPtr, alphaImage.RowPitch);
689 throw new NotImplementedException();
691 Log.Info(
"Extracting region and exporting to Paradox Image ...");
694 region.X = Math.Max(0, Math.Min(region.X, texImage.Width));
695 region.Y = Math.Max(0, Math.Min(region.Y, texImage.Height));
696 region.Width = Math.Max(0, Math.Min(region.Width, texImage.Width - region.X));
697 region.Height = Math.Max(0, Math.Min(region.Height, texImage.Height - region.Y));
700 var pdxImage = Image.New2D(region.Width, region.Height, 1, texImage.Format);
701 if (pdxImage == null)
703 Log.Error(
"Image could not be created.");
704 throw new InvalidOperationException(
"Image could not be created.");
708 var pixelBuffer = pdxImage.GetPixelBuffer(0, 0);
709 var dstRowPitch = pixelBuffer.RowStride;
714 var rowSrcPtr = texImage.SubImageArray[0].Data;
715 var rowDstPtr = pdxImage.DataPointer;
716 rowSrcPtr = IntPtr.Add(rowSrcPtr, region.Y * texImage.RowPitch);
717 for (
int i = 0; i < region.Height; i++)
719 var pSrc = ((UInt32*)rowSrcPtr) + region.X;
720 var pDst = (UInt32*)rowDstPtr;
722 for (
int x = 0; x < region.Width; x++)
723 *(pDst++) = *(pSrc++);
725 rowSrcPtr = IntPtr.Add(rowSrcPtr, texImage.RowPitch);
726 rowDstPtr = IntPtr.Add(rowDstPtr, dstRowPitch);
742 var request =
new ExportToParadoxRequest();
744 ExecuteRequest(image, request);
746 return request.PdxImage;
759 Log.Error(
"The gamma must be a positive float.");
763 if (Tools.IsCompressed(image.
Format))
765 Log.Warning(
"You can't correct gamme on a compressed texture. It will be decompressed first..");
769 var request =
new GammaCorrectionRequest(gamma);
771 ExecuteRequest(image, request);
782 if (Tools.IsCompressed(image.
Format))
784 Log.Warning(
"You can't flip a compressed texture. It will be decompressed first..");
788 var request =
new FlippingRequest(orientation);
790 ExecuteRequest(image, request);
801 if (Tools.IsCompressed(image.
Format))
803 Log.Warning(
"You can't flip a compressed texture. It will be decompressed first..");
807 var request =
new FlippingSubRequest(index, orientation);
809 ExecuteRequest(image, request);
821 var request =
new SwappingRequest(firstIndex, secondIndex);
823 ExecuteRequest(image, request);
833 private ITexLibrary FindLibrary(
TexImage image, IRequest request)
835 foreach (var library
in textureLibraries)
837 if (library.CanHandleRequest(image, request))
861 if (name == null || name.Equals(
""))
863 Log.Error(
"You must enter a texture name to extract.");
867 if (minimumMipmapSize < 0)
869 Log.Error(
"The minimup Mipmap size can't be negative. Put 0 or 1 for a complete Mipmap chain.");
870 throw new TextureToolsException(
"The minimup Mipmap size can't be negative. Put 0 or 1 for a complete Mipmap chain.");
875 if(Tools.IsCompressed(atlas.
Format))
880 ExecuteRequest(atlas, request);
882 return request.Texture;
900 if (indice < 0 || indice > array.
ArraySize-1)
902 Log.Error(
"The indice you entered is not valid.");
906 if (minimumMipmapSize < 0)
908 Log.Error(
"The minimup Mipmap size can't be negative. Put 0 or 1 for a complete Mipmap chain.");
909 throw new TextureToolsException(
"The minimup Mipmap size can't be negative. Put 0 or 1 for a complete Mipmap chain.");
914 ExecuteRequest(array, request);
916 return request.Texture;
929 if (minimumMipmapSize < 0)
931 Log.Error(
"The minimup Mipmap size can't be negative. Put 0 or 1 for a complete Mipmap chain.");
932 throw new TextureToolsException(
"The minimup Mipmap size can't be negative. Put 0 or 1 for a complete Mipmap chain.");
937 if (Tools.IsCompressed(atlas.
Format))
939 Log.Warning(
"You can't extract a texture from a compressed atlas. The atlas will be decompressed first..");
943 ExecuteRequest(atlas, request);
945 return request.Textures;
958 if (minimumMipmapSize < 0)
960 Log.Error(
"The minimup Mipmap size can't be negative. Put 0 or 1 for a complete Mipmap chain.");
961 throw new TextureToolsException(
"The minimup Mipmap size can't be negative. Put 0 or 1 for a complete Mipmap chain.");
966 if (Tools.IsCompressed(array.
Format))
971 ExecuteRequest(array, request);
973 return request.Textures;
992 if (texture.
Name.Equals(
"") && name.Equals(
""))
994 Log.Error(
"You must either set the Name attribute of the TexImage, or you must give the name of the texture to update in the atlas.");
995 throw new TextureToolsException(
"You must either set the Name attribute of the TexImage, or you must give the name of the texture to update in the atlas.");
1000 Log.Error(
"The new texture can't be a texture array.");
1004 CheckConformity(atlas, texture);
1006 name = name.Equals(
"") ? texture.
Name : name;
1029 Log.Error(
"The first given texture must be an array texture.");
1035 Log.Error(
"The given indice is out of range in the array texture.");
1039 CheckConformity(array, texture);
1056 if (indice < 0 || indice > array.
ArraySize)
1058 Log.Error(
"The given indice must be between 0 and " + array.ArraySize);
1062 CheckConformity(array, texture);
1082 Log.Error(
"The array size must be > 1.");
1086 if (indice < 0 || indice > array.
ArraySize-1)
1088 Log.Error(
"The given indice must be between 0 and " + array.ArraySize);
1105 Log.Warning(
"The given texture has no mipmaps. They will be generated..");
1111 Log.Warning(
"The given texture format isn't correct. The texture will be converted..");
1112 if (Tools.IsCompressed(model.
Format))
1132 private void ExecuteRequest(TexImage image, IRequest request)
1135 if (image.CurrentLibrary != null && image.CurrentLibrary.CanHandleRequest(image, request))
1137 image.CurrentLibrary.Execute(image, request);
1141 ITexLibrary library;
1142 if ((library = FindLibrary(image, request)) != null)
1144 if (Tools.IsInBGRAOrder(image.Format) && !library.SupportBGRAOrder())
1146 SwitchChannel(image);
1149 if(image.CurrentLibrary != null) image.CurrentLibrary.EndLibrary(image);
1151 library.StartLibrary(image);
1153 library.Execute(image, request);
1155 image.CurrentLibrary = library;
1159 Log.Error(
"No available library could perform the task : " + request.Type);
1160 throw new TextureToolsException(
"No available library could perform the task : " + request.Type);
1166 static void Main(
string[] args)
1168 var texTool =
new TextureTool();
1302 catch (TextureToolsException)
TexImage CreateTextureArray(List< TexImage > textureList)
Creates a texture array with the given TexImage.
SiliconStudio.Paradox.Graphics.Image ConvertToParadoxImage(TexImage image)
Converts to paradox image.
TextureQuality
The desired texture quality.
List< TexImage > ExtractAll(TexImage array, int minimumMipmapSize=1)
Extracts every TexImage included in the array.
Object Clone()
Creates a new object that is a copy of the current instance.
void Dispose()
Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resourc...
void SwitchChannel(TexImage image)
Switches the channel R and B.
void Resize(TexImage image, int width, int height, Filter.Rescaling filter)
Resizes the specified image to a fixed image size.
MipMapGeneration
Available filters for mipmap generation
TextureDimension
The Different types of texture
void ColorKey(TexImage image, Color colorKey)
Apply a color key on the image by replacing the color passed by to this method by a white transparent...
Rescaling
Available filters for rescaling operation
Provides method to load images or textures, to modify them and to convert them with different texture...
Provides method to instantiate an image 1D/2D/3D supporting TextureArray and mipmaps on the CPU or to...
TexImage Load(SiliconStudio.Paradox.Graphics.Image image)
Loads the specified image of the class SiliconStudio.Paradox.Graphics.Image.
void Save(TexImage image, String fileName, int minimumMipMapSize=1)
Saves the specified TexImage into a file.
Orientation
Available Orientation to flip textures
Allows the creation and manipulation of texture arrays.
void Flip(TexImage image, Orientation orientation)
Flips the specified image horizontally or vertically.
void Decompress(TexImage image)
Decompresses the specified TexImage.
void PreMultiplyAlpha(TexImage image)
Premultiplies the alpha.
void CorrectGamma(TexImage image, double gamma)
Corrects the gamma.
TexImage GenerateNormalMap(TexImage heightMap, float amplitude)
Generates the normal map.
void Insert(TexImage array, TexImage texture, int indice)
Inserts a texture into a texture array at a specified position.
TexAtlas LoadAtlas(TexAtlas.TexLayout layout, string file)
Loads the Atlas corresponding to the specified layout and file.
unsafe TexImage CreateImageFromAlphaComponent(TexImage texImage)
Create a new image from the alpha component of a reference image.
HRESULT GenerateMipMaps(_In_ const Image &baseImage, _In_ DWORD filter, _In_ size_t levels, _Inout_ ScratchImage &mipChain, _In_ bool allow1D=false)
TexImage Extract(TexAtlas atlas, string name, int minimumMipmapSize=1)
Extracts the TexImage corresponding to the specified name in the given atlas.
TextureDimension Dimension
Base implementation for ILogger.
TexAtlas LoadAtlas(string file, string layoutFile="")
Loads the Atlas corresponding to the specified texture file and layout file.
Request to insert a specific texture in a texture array.
List< TexImage > ExtractAll(TexAtlas atlas, int minimumMipmapSize=1)
Extracts every TexImage included in the atlas.
void Remove(TexImage array, int indice)
Removes the texture at a specified position from a texture array.
Request to create a texture array from a texture list.
TexAtlas CreateAtlas(List< TexImage > textureList, bool forceSquaredAtlas=false)
Creates an atlas with the given TexImage.
TexImage Load(string file)
Loads the specified file.
Request to remove the texture at a specified position from a texture array.
void GenerateMipMaps(TexImage image, Filter.MipMapGeneration filter)
Generates the mip maps.
Request to update a specific texture in a texture array.
Request to create a texture cube from a texture list.
Represents a 32-bit color (4 bytes) in the form of RGBA (in byte order: R, G, B, A).
A texture atlas : a texture made from a composition of many textures.
Request to premultiply the alpha on the texture
HRESULT Decompress(_In_ const Image &cImage, _In_ DXGI_FORMAT format, _Out_ ScratchImage &image)
TexImage Extract(TexImage array, int indice, int minimumMipmapSize=1)
Extracts the TexImage corresponding to the specified indice in the given texture array.
Request to create an atlas from a texture list.
TexAtlas RetrieveAtlas(TexImage texture, string layoutFile)
Retrieves the atlas from a TexImage and its corresponding layout file.
Temporary format containing texture data and information. Used as buffer between texture libraries...
TextureTool()
Initializes a new instance of the TextureTool class.
Provides enumerations of the different available filter types.
SiliconStudio.Paradox.Graphics.PixelFormat Format
void FlipSub(TexImage image, int index, Orientation orientation)
Flips the specified image horizontally or vertically.
unsafe Image CreateImageFromRegion(TexImage texImage, Rectangle region)
Create a new image from region.
void Rescale(TexImage image, float widthFactor, float heightFactor, Filter.Rescaling filter)
Rescales the specified image with the specified factors.
_In_ size_t _In_ size_t _In_ DXGI_FORMAT format
Structure using the same layout than System.Drawing.Rectangle
void Swap(TexImage image, int firstIndex, int secondIndex)
Swaps two slices of a texture array.
Request to premultiply the alpha on the texture
Output message to log right away.
PixelFormat
Defines various types of pixel formats.
void Update(TexAtlas atlas, TexImage texture, string name="")
Updates a specific texture in the atlas with the given TexImage.
HRESULT Compress(_In_ const Image &srcImage, _In_ DXGI_FORMAT format, _In_ DWORD compress, _In_ float alphaRef, _Out_ ScratchImage &cImage)
Request to update a texture from an atlas
void Update(TexImage array, TexImage texture, int indice)
Updates a specific texture in the texture array with the given TexImage.
TexImage CreateTextureCube(List< TexImage > textureList)
Creates a texture cube with the given TexImage.
A LogListener implementation redirecting its output to the default OS console. If console is not supp...
void Save(TexImage image, String fileName, SiliconStudio.Paradox.Graphics.PixelFormat format, int minimumMipMapSize=1)
Saves the specified TexImage into a file with the specified format.
void Compress(TexImage image, SiliconStudio.Paradox.Graphics.PixelFormat format, TextureQuality quality=TextureQuality.Fast)
Compresses the specified image into the specified format.