public void TestEquals() { TexImage image2 = new TexImage(new IntPtr(), 699104, 512, 512, 1, SiliconStudio.Paradox.Graphics.PixelFormat.BC3_UNorm, 10, 2, TexImage.TextureDimension.Texture2D); Assert.IsTrue(image.Equals(image2)); image2 = new TexImage(new IntPtr(), 699104, 512, 256, 1, SiliconStudio.Paradox.Graphics.PixelFormat.BC3_UNorm, 10, 2, TexImage.TextureDimension.Texture2D); Assert.IsFalse(image.Equals(image2)); }
/// <summary> /// Initializes a new instance of the <see cref="TexAtlas"/> class. /// </summary> /// <param name="layout">The layout.</param> /// <param name="atlas">The atlas.</param> public TexAtlas(TexLayout layout, TexImage atlas) : base(atlas.Data, atlas.DataSize, atlas.Width, atlas.Height, atlas.Depth, atlas.Format, atlas.MipmapCount, atlas.ArraySize, atlas.Dimension, atlas.FaceCount) { RowPitch = atlas.RowPitch; SlicePitch = atlas.SlicePitch; SubImageArray = atlas.SubImageArray; Name = atlas.Name; DisposingLibrary = atlas.DisposingLibrary; CurrentLibrary = atlas.CurrentLibrary; LibraryData = atlas.LibraryData; Layout = layout; Name = ""; }
private void HandleResizing(TextureTool texTool, TexImage image) { if (Width != null && Height != null) { bool targetInPercent; var width = ParsePixelSize(Width, out targetInPercent); var height = ParsePixelSize(Height, out targetInPercent); if (targetInPercent) { texTool.Rescale(image, width / 100f, height / 100f, RescalingFilter); } else { texTool.Resize(image, width, height, RescalingFilter); } } else if (Width != null && Height == null) { bool targetInPercent; var width = ParsePixelSize(Width, out targetInPercent); if (targetInPercent) { texTool.Rescale(image, width / 100f, 1, RescalingFilter); } else { texTool.Resize(image, width, image.Height, RescalingFilter); } } else if (Width == null && Height != null) { bool targetInPercent; var height = ParsePixelSize(Height, out targetInPercent); if (targetInPercent) { texTool.Rescale(image, 1, height / 100f, RescalingFilter); } else { texTool.Resize(image, image.Width, height, RescalingFilter); } } }
public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) { return(false); } if (ReferenceEquals(this, obj)) { return(true); } if (obj.GetType() != this.GetType()) { return(false); } TexImage img = (TexImage)obj; if (SubImageArray.Length != img.SubImageArray.Length) { return(false); } for (int i = 0; i < SubImageArray.Length; ++i) { if (!(SubImageArray[i].DataSize == img.SubImageArray[i].DataSize && SubImageArray[i].Width == img.SubImageArray[i].Width && SubImageArray[i].Height == img.SubImageArray[i].Height && SubImageArray[i].RowPitch == img.SubImageArray[i].RowPitch && SubImageArray[i].SlicePitch == img.SubImageArray[i].SlicePitch)) { return(false); } } return(Width == img.Width && Height == img.Height && Depth == img.Depth && Format == img.Format && MipmapCount == img.MipmapCount && ArraySize == img.ArraySize && FaceCount == img.FaceCount && Dimension == img.Dimension && DataSize == img.DataSize && RowPitch == img.RowPitch && SlicePitch == img.SlicePitch); }
/// <summary> /// Removes the texture at a specified position from a texture array. /// </summary> /// <param name="array">The array.</param> /// <param name="indice">The indice.</param> /// <exception cref="TextureToolsException"> /// The array size must be > 1. /// or /// The given indice must be between 0 and + array.ArraySize /// </exception> public void Remove(TexImage array, int indice) { if (array.ArraySize == 1) { Log.Error("The array size must be > 1."); throw new TextureToolsException("The array size must be > 1."); } if (indice < 0 || indice > array.ArraySize-1) { Log.Error("The given indice must be between 0 and " + array.ArraySize); throw new TextureToolsException("The given indice must be between 0 and " + array.ArraySize); } ExecuteRequest(array, new ArrayElementRemovalRequest(indice)); }
/// <summary> /// Converts to paradox image. /// </summary> /// <param name="image">The image.</param> /// <returns>The converted Paradox <see cref="SiliconStudio.Paradox.Graphics.Image"/>.</returns> /// <remarks>The user is the owner of the returned image, and has to dispose it after he finishes using it</remarks> public SiliconStudio.Paradox.Graphics.Image ConvertToParadoxImage(TexImage image) { var request = new ExportToParadoxRequest(); ExecuteRequest(image, request); return request.PdxImage; }
/// <summary> /// Flips the specified image horizontally or vertically. /// </summary> /// <param name="image">The image.</param> /// <param name="index">The index of the sub-image.</param> /// <param name="orientation">The orientation <see cref="Orientation.Flip"/>.</param> public void FlipSub(TexImage image, int index, Orientation orientation) { if (image.Format.IsCompressed()) { Log.Warning("You can't flip a compressed texture. It will be decompressed first.."); Decompress(image, image.Format.IsSRgb()); } var request = new FlippingSubRequest(index, orientation); ExecuteRequest(image, request); }
/// <summary> /// Create a new image from the alpha component of a reference image. /// </summary> /// <param name="texImage">The image from which to take the alpha</param> /// <returns>The <see cref="TexImage"/> containing the alpha component as rgb color. Note: it is the user responsibility to dispose the returned image.</returns> public unsafe TexImage CreateImageFromAlphaComponent(TexImage texImage) { if (texImage.Dimension != TexImage.TextureDimension.Texture2D || texImage.Format.IsCompressed()) throw new NotImplementedException(); var alphaImage = (TexImage)texImage.Clone(true); var rowPtr = alphaImage.Data; for (int i = 0; i < alphaImage.Height; i++) { var pByte = (byte*)rowPtr; for (int x = 0; x < alphaImage.Width; x++) { pByte[0] = pByte[3]; pByte[1] = pByte[3]; pByte[2] = pByte[3]; pByte += 4; } rowPtr = IntPtr.Add(rowPtr, alphaImage.RowPitch); } return alphaImage; }
/// <summary> /// Find the region of the texture containing the sprite under the specified pixel. /// </summary> /// <param name="texture">The texture containing the sprite</param> /// <param name="pixel">The coordinate of the pixel specifying the sprite</param> /// <param name="separatorColor">The separator color that delimit the sprites. If null the <see cref="Color.Transparent"/> color is used</param> /// <param name="separatorMask">The mask specifying which bits of the color should be checked. The bits are ordered as AABBGGRR.</param> /// <returns></returns> public unsafe Rectangle FindSpriteRegion(TexImage texture, Int2 pixel, Color? separatorColor = null, uint separatorMask = 0xff000000) { if (texture == null) throw new ArgumentNullException(nameof(texture)); var format = texture.Format; if (texture.Dimension != TexImage.TextureDimension.Texture2D || !(format.IsRGBAOrder() || format.IsBGRAOrder() || format.SizeInBytes() != 4)) throw new NotImplementedException(); // adjust the separator color the mask depending on the color format. var separator = (uint)(separatorColor ?? Color.Transparent).ToRgba(); if(texture.Format.IsBGRAOrder()) { separator = RgbaToBgra(separator); separatorMask = RgbaToBgra(separatorMask); } var maskedSeparator = separator & separatorMask; var ptr = (uint*)texture.Data; var stride = texture.RowPitch / 4; // check for empty region (provided pixel is not valid) var textureRegion = new Rectangle(0, 0, texture.Width, texture.Height); if (!textureRegion.Contains(pixel) || (ptr[pixel.Y * stride + pixel.X] & separatorMask) == maskedSeparator) return new Rectangle(pixel.X, pixel.Y, 0, 0); // initialize the region with the provided pixel var region = new Rectangle(pixel.X, pixel.Y, 1, 1); var nextSearchOffsets = new[,] { { new Int2(-1, -1), new Int2( 0, -1) }, { new Int2( 1, -1), new Int2( 1, 0) }, { new Int2( 1, 1), new Int2( 0, 1) }, { new Int2(-1, 1), new Int2(-1, 0) } }; var contourLeftEgde = pixel; var rotationDirection = 0; do { // Stage 1: Find an edge of the shape (look to the left of the provided pixel as long as possible) var startEdge = contourLeftEgde; var startEdgeDirection = EdgeDirection.Left; for (int x = startEdge.X; x >= 0; --x) { if ((ptr[startEdge.Y * stride + x] & separatorMask) == maskedSeparator) break; startEdge.X = x; } // Stage 2: Determine the whole contour of the shape and update the region. // Note: the found contour can correspond to an internal hole contour or the external shape contour. var currentEdge = startEdge; var currentEdgeDirection = startEdgeDirection; do { var previousEdgeDirection = currentEdgeDirection; var diagonalPixel = currentEdge + nextSearchOffsets[(int)currentEdgeDirection, 0]; var diagonalIsSeparator = !textureRegion.Contains(diagonalPixel) || (ptr[diagonalPixel.Y * stride + diagonalPixel.X] & separatorMask) == maskedSeparator; var neighbourPixel = currentEdge + nextSearchOffsets[(int)currentEdgeDirection, 1]; var neighbourIsSeparator = !textureRegion.Contains(neighbourPixel) || (ptr[neighbourPixel.Y * stride + neighbourPixel.X] & separatorMask) == maskedSeparator; // determine the next edge position if (!diagonalIsSeparator) { currentEdge = diagonalPixel; currentEdgeDirection = (EdgeDirection)(((int)currentEdgeDirection + 3) % 4); } else if (!neighbourIsSeparator) { currentEdge = neighbourPixel; } else { currentEdgeDirection = (EdgeDirection)(((int)currentEdgeDirection + 1) % 4); } // keep record of the point of the edge which is if (currentEdge.X < contourLeftEgde.X) contourLeftEgde = currentEdge; // increase or decrease the rotation counter based on the sequence of edge direction rotationDirection += RotationDirection(previousEdgeDirection, currentEdgeDirection); // update the rectangle region = Rectangle.Union(region, currentEdge); } while (currentEdge != startEdge || currentEdgeDirection != startEdgeDirection); // as long as we do not close the contour continue to explore } // repeat the process as long as the edge found is not the shape external contour. while (rotationDirection != 4); return region; }
/// <summary> /// Resizes the specified image to a fixed image size. /// </summary> /// <remarks> /// If the image is in a compressed format, it will be first decompressed. /// </remarks> /// <param name="image">The image.</param> /// <param name="width">The width.</param> /// <param name="height">The height.</param> /// <param name="filter">The filter.</param> public void Resize(TexImage image, int width, int height, Filter.Rescaling filter) { if (width < 1 || height < 1) { Log.Error("The new size must be an integer > 0."); throw new TextureToolsException("The new size must be an integer > 0."); } // Texture already has the requested dimension if (image.Width == width && image.Height == height) { return; } if (image.Format.IsCompressed()) { Log.Warning("You can't resize a compressed texture. It will be decompressed first.."); Decompress(image, image.Format.IsSRgb()); } ExecuteRequest(image, new FixedRescalingRequest(width, height, filter)); }
/// <summary> /// Generates the normal map. /// </summary> /// <param name="heightMap">The height map.</param> /// <param name="amplitude">The amplitude.</param> /// <returns>An instance of <see cref="TexImage"/> containig the normal map.</returns> public TexImage GenerateNormalMap(TexImage heightMap, float amplitude) { if (amplitude <= 0) { Log.Error("The amplitude must be a positive float."); throw new TextureToolsException("The amplitude must be a positive float."); } if (heightMap.Format.IsCompressed()) { Log.Warning("You can't generate a normal map from a compressed height hmap. It will be decompressed first.."); Decompress(heightMap, heightMap.Format.IsSRgb()); } var request = new NormalMapGenerationRequest(amplitude); ExecuteRequest(heightMap, request); return request.NormalMap; }
/// <summary> /// Switches the channel R and B. /// </summary> /// <remarks> /// PVR texture and ATC library can't handle BGRA order, channels B and R must be switched to get the new order RGBA. (This switch is made automatically) /// If the image is in a compressed format, it will be first decompressed. /// </remarks> /// <param name="image">The image.</param> public void SwitchChannel(TexImage image) { if (image.Format.IsCompressed()) { Log.Warning("You can't switch channels of a compressed texture. It will be decompressed first.."); Decompress(image, image.Format.IsSRgb()); } ExecuteRequest(image, new SwitchingBRChannelsRequest()); }
/// <summary> /// Apply a color key on the image by replacing the color passed by to this method by a white transparent color (Alpha is 0). /// </summary> /// <param name="image">The image.</param> /// <param name="colorKey">The color key.</param> public void ColorKey(TexImage image, Color colorKey) { if (image.Format.IsCompressed()) { Log.Warning("You can't compress an already compressed texture. It will be decompressed first.."); Decompress(image, image.Format.IsSRgb()); } if (image.Format != PixelFormat.R8G8B8A8_UNorm && image.Format != PixelFormat.B8G8R8A8_UNorm && image.Format != PixelFormat.B8G8R8A8_UNorm_SRgb && image.Format != PixelFormat.R8G8B8A8_UNorm_SRgb) { Log.Error("ColorKey TextureConverter is only supporting R8G8B8A8_UNorm or B8G8R8A8_UNorm while Texture Format is [{0}]", image.Format); return; } var request = new ColorKeyRequest(colorKey); ExecuteRequest(image, request); }
/// <summary> /// Inserts a texture into a texture array at a specified position. /// </summary> /// <param name="array">The array.</param> /// <param name="texture">The texture to be added.</param> /// <param name="indice">The indice.</param> /// <exception cref="TextureToolsException">The given indice must be between 0 and the array size</exception> public void Insert(TexImage array, TexImage texture, int indice) { texture.Update(); if (indice < 0 || indice > array.ArraySize) { Log.Error("The given indice must be between 0 and " + array.ArraySize); throw new TextureToolsException("The given indice must be between 0 and " + array.ArraySize); } CheckConformity(array, texture); ExecuteRequest(array, new ArrayInsertionRequest(texture, indice)); }
/// <summary> /// Retrieves the atlas from a TexImage and its corresponding layout file. /// </summary> /// <param name="texture">The texture.</param> /// <param name="layoutFile">The layout file.</param> /// <returns>An instance of <see cref="TexAtlas"/>.</returns> /// <exception cref="TextureToolsException">The layout file doesn't exist. Please check the file path.</exception> public TexAtlas RetrieveAtlas(TexImage texture, string layoutFile) { if (!File.Exists(layoutFile)) { Log.Error("The file " + layoutFile + " doesn't exist. Please check the file path."); throw new TextureToolsException("The file " + layoutFile + " doesn't exist. Please check the file path."); } return new TexAtlas(TexAtlas.TexLayout.Import(layoutFile), texture); }
/// <summary> /// Updates a specific texture in the texture array with the given TexImage. /// </summary> /// <param name="array">The array.</param> /// <param name="texture">The texture.</param> /// <param name="indice">The indice.</param> /// <exception cref="TextureToolsException"> /// The first given texture must be an array texture. /// or /// The given indice is out of range in the array texture. /// </exception> public void Update(TexImage array, TexImage texture, int indice) { texture.Update(); if (array.ArraySize == 1) { Log.Error("The first given texture must be an array texture."); throw new TextureToolsException("The first given texture must be an array texture."); } if (array.ArraySize-1 < indice) { Log.Error("The given indice is out of range in the array texture."); throw new TextureToolsException("The given indice is out of range in the array texture."); } CheckConformity(array, texture); ExecuteRequest(array, new ArrayUpdateRequest(texture, indice)); }
public void Run(string[] args) { // Print the exe header PrintHeader(); foreach (String s in args) { Console.WriteLine(s); } Console.WriteLine(""); // Parse the command line if (!ParseCommandLine(args)) { Environment.Exit(-1); } // Check if we have a pattern InputPatternFile = Path.Combine(Environment.CurrentDirectory, InputPatternFile); int indexOfPattern = InputPatternFile.IndexOf('*'); bool isPattern = indexOfPattern >= 0; if (!isPattern) { InputPatternFile = Path.GetFullPath(InputPatternFile); } OutputFileOrDirectory = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, OutputFileOrDirectory)); var inputOutputFiles = new List <Tuple <string, string> >(); if (isPattern) { if (!Directory.Exists(OutputFileOrDirectory)) { Directory.CreateDirectory(OutputFileOrDirectory); } var directory = InputPatternFile.Substring(0, indexOfPattern); var pattern = InputPatternFile.Substring(indexOfPattern, InputPatternFile.Length - indexOfPattern); foreach (var file in Directory.EnumerateFiles(directory, pattern, SearchOption.AllDirectories)) { var outputFile = Path.Combine(OutputFileOrDirectory, file.Substring(directory.Length, file.Length - directory.Length)); bool excludeFile = false; foreach (var excludeItem in ExcludeList) { if (file.IndexOf(excludeItem, StringComparison.InvariantCultureIgnoreCase) >= 0) { excludeFile = true; break; } } if (!excludeFile) { inputOutputFiles.Add(new Tuple <string, string>(file, outputFile)); } } } else { inputOutputFiles.Add(new Tuple <string, string>(InputPatternFile, OutputFileOrDirectory)); } var texTool = new TextureTool(); GlobalLogger.GlobalMessageLogged += new ConsoleLogListener(); bool hasErrors = false; foreach (var inputOutputFile in inputOutputFiles) { var inputFile = inputOutputFile.Item1; var outputFile = inputOutputFile.Item2; TexImage image = null; try { image = texTool.Load(inputFile, IsSRgb); HandleResizing(texTool, image); if (FlipOrientation.HasValue) { texTool.Flip(image, FlipOrientation.Value); } if (MipMap) { texTool.GenerateMipMaps(image, MipMapFilter); } if (PreMulAlpha) { texTool.PreMultiplyAlpha(image); } if (TextureFormat.HasValue) { texTool.Compress(image, TextureFormat.Value); } texTool.Save(image, outputFile, MipMapSize); } catch (TextureToolsException) { hasErrors = true; } finally { if (image != null) { image.Dispose(); } } } texTool.Dispose(); if (hasErrors) { //Environment.Exit(-1); } }
/// <summary> /// Creates a new object that is a copy of the current instance. /// </summary> /// <param name="CopyMemory">if set to <c>true</c> [copy memory], it is a DEEP copy.</param> /// <returns> /// A new object that is a copy of this instance. /// </returns> virtual public Object Clone(bool CopyMemory) { if (this.CurrentLibrary != null) { this.CurrentLibrary.EndLibrary(this); this.CurrentLibrary = null; } TexImage newTex = new TexImage() { // Basic infos Data = CopyMemory?System.Runtime.InteropServices.Marshal.AllocHGlobal(this.DataSize):this.Data, DataSize = this.DataSize, Width = this.Width, Height = this.Height, Depth = this.Depth, RowPitch = this.RowPitch, SlicePitch = this.SlicePitch, Format = this.Format, OriginalAlphaDepth = this.OriginalAlphaDepth, // Texture infos ArraySize = this.ArraySize, FaceCount = this.FaceCount, MipmapCount = this.MipmapCount, SubImageArray = new SubImage[this.SubImageArray.Length], Dimension = this.Dimension, Name = this.Name, // ITexLibrary Data DisposingLibrary = this.DisposingLibrary, CurrentLibrary = this.CurrentLibrary, LibraryData = new Dictionary <ITexLibrary, ITextureLibraryData>(), // Disposing info Disposed = this.Disposed, }; if (CopyMemory) { Utilities.CopyMemory(newTex.Data, this.Data, this.DataSize); } int offset = 0; for (int i = 0; i < this.SubImageArray.Length; ++i) { newTex.SubImageArray[i] = this.SubImageArray[i]; if (CopyMemory) { newTex.SubImageArray[i].Data = new IntPtr(newTex.Data.ToInt64() + offset); } offset += newTex.SubImageArray[i].DataSize; } if (CopyMemory && this.DisposingLibrary != null) { this.DisposingLibrary.StartLibrary(newTex); } else if (!CopyMemory) { newTex.DisposingLibrary = null; } return(newTex); }
/// <summary> /// Compresses the specified image into the specified format. /// </summary> /// <remarks> /// If the image is in a compressed format, it will be first decompressed. /// If the compressing library doesn't support BGRA order and the current image format is in this order, the channels R and B will be switched. /// </remarks> /// <param name="image">The image.</param> /// <param name="format">The format.</param> public void Compress(TexImage image, PixelFormat format, TextureQuality quality = TextureQuality.Fast) { if (image.Format == format) return; if (image.Format.IsCompressed()) { Log.Warning("You can't compress an already compressed texture. It will be decompressed first.."); Decompress(image, format.IsSRgb()); } var request = new CompressingRequest(format, quality); ExecuteRequest(image, request); }
/// <summary> /// Checks the conformity of a candidate texture with a model one : check the mipmap count and the format. /// </summary> /// <param name="model">The model.</param> /// <param name="candidate">The candidate.</param> private void CheckConformity(TexImage model, TexImage candidate) { if (model.MipmapCount > 1 && candidate.MipmapCount == 1) { Log.Warning("The given texture has no mipmaps. They will be generated.."); GenerateMipMaps(candidate, Filter.MipMapGeneration.Box); } if (candidate.Format != model.Format) { Log.Warning("The given texture format isn't correct. The texture will be converted.."); if (model.Format.IsCompressed()) { if (candidate.Format.IsCompressed()) Decompress(candidate, candidate.Format.IsSRgb()); Compress(candidate, model.Format); } else { Decompress(candidate, candidate.Format.IsSRgb()); if (candidate.Format != model.Format) Compress(candidate, model.Format); } } }
/// <summary> /// Generates the mip maps. /// </summary> /// <remarks> /// If the image is in a compressed format, it will be first decompressed. /// </remarks> /// <param name="image">The image.</param> /// <param name="filter">The filter.</param> public void GenerateMipMaps(TexImage image, Filter.MipMapGeneration filter) { if (image.Format.IsCompressed()) { Log.Warning("You can't generate mipmaps for a compressed texture. It will be decompressed first.."); Decompress(image, image.Format.IsSRgb()); } ExecuteRequest(image, new MipMapsGenerationRequest(filter)); }
/// <summary> /// Executes the request. /// </summary> /// <param name="image">The image.</param> /// <param name="request">The request.</param> /// <exception cref="TextureToolsException">No available library could perform the task : + request.Type</exception> private void ExecuteRequest(TexImage image, IRequest request) { // First Check if the current library can handle the request if (image.CurrentLibrary != null && image.CurrentLibrary.CanHandleRequest(image, request)) { image.CurrentLibrary.Execute(image, request); } else // Otherwise, it finds another library which can handle the request { ITexLibrary library; if ((library = FindLibrary(image, request)) != null) { if (image.Format.IsBGRAOrder() && !library.SupportBGRAOrder()) { SwitchChannel(image); } if(image.CurrentLibrary != null) image.CurrentLibrary.EndLibrary(image); // Ending the use of the previous library (mainly to free memory) library.StartLibrary(image); // Preparing the new library : converting TexImage format to the library native format library.Execute(image, request); image.CurrentLibrary = library; } else // If no library could be found, an exception is thrown { Log.Error("No available library could perform the task : " + request.Type); throw new TextureToolsException("No available library could perform the task : " + request.Type); } } }
/// <summary> /// Rescales the specified image with the specified factors. /// </summary> /// <remarks> /// The new size will be : width = width * widthFactor and height = height * heightFactor /// If the image is in a compressed format, it will be first decompressed. /// </remarks> /// <param name="image">The image.</param> /// <param name="widthFactor">The width factor.</param> /// <param name="heightFactor">The height factor.</param> /// <param name="filter">The filter.</param> public void Rescale(TexImage image, float widthFactor, float heightFactor, Filter.Rescaling filter) { if (widthFactor <= 0 || heightFactor <= 0) { Log.Error("The size factors must be positive floats."); throw new TextureToolsException("The size factors must be positive floats."); } // The texture dimension won't change. if (widthFactor == 1 && heightFactor ==1) { return; } if (image.Format.IsCompressed()) { Log.Warning("You can't rescale a compressed texture. It will be decompressed first.."); Decompress(image, image.Format.IsSRgb()); } ExecuteRequest(image, new FactorRescalingRequest(widthFactor, heightFactor, filter)); }
/// <summary> /// Creates a texture array with the given TexImage. /// </summary> /// <param name="textureList">The texture list.</param> /// <returns>An instance of <see cref="TexImage"/> corresponding containing the texture array.</returns> /// <exception cref="TextureToolsException"> /// No available library could create the array. /// or /// The textures must all have the same size and format to be in a texture array. /// </exception> public TexImage CreateTextureArray(List<TexImage> textureList) { var array = new TexImage(); var request = new ArrayCreationRequest(textureList); ITexLibrary library = FindLibrary(array, request); if (library == null) { Log.Error("No available library could create the array."); throw new TextureToolsException("No available library could create the array."); } int width = textureList[0].Width; int height = textureList[0].Height; int depth = textureList[0].Depth; array.Format = textureList[0].Format; foreach (var texture in textureList) { texture.Update(); if (texture.Width != width || texture.Height != height || texture.Depth != depth || texture.Format != array.Format) { Log.Error("The textures must all have the same size and format to be in a texture array."); throw new TextureToolsException("The textures must all have the same size and format to be in a texture array."); } } ExecuteRequest(array, request); return array; }
/// <summary> /// Premultiplies the alpha. /// </summary> /// <param name="image">The image.</param> public void PreMultiplyAlpha(TexImage image) { if (image.Format.IsCompressed()) { Log.Warning("You can't premultiply alpha on a compressed texture. It will be decompressed first.."); Decompress(image, image.Format.IsSRgb()); } ExecuteRequest(image, new PreMultiplyAlphaRequest()); }
/// <summary> /// Creates a texture cube with the given TexImage. /// </summary> /// <param name="textureList">The texture list.</param> /// <returns>An instance of <see cref="TexImage"/> containing the texture cube.</returns> /// <exception cref="TextureToolsException"> /// No available library could create the cube. /// or /// The number of texture in the texture list must be a multiple of 6. /// or /// The textures must all have the same size and format to be in a texture cube. /// </exception> public TexImage CreateTextureCube(List<TexImage> textureList) { var cube = new TexImage(); var request = new CubeCreationRequest(textureList); if (textureList.Count % 6 != 0) { Log.Error("The number of texture in the texture list must be a multiple of 6."); throw new TextureToolsException("The number of texture in the texture list must be a multiple of 6."); } ITexLibrary library = FindLibrary(cube, request); if (library == null) { Log.Error("No available library could create the cube."); throw new TextureToolsException("No available library could create the cube."); } int width = textureList[0].Width; int height = textureList[0].Height; int depth = textureList[0].Depth; cube.Format = textureList[0].Format; foreach (var texture in textureList) { texture.Update(); if (texture.Width != width || texture.Height != height || texture.Depth != depth || texture.Format != cube.Format) { Log.Error("The textures must all have the same size and format to be in a texture cube."); throw new TextureToolsException("The textures must all have the same size and format to be in a texture cube."); } } ExecuteRequest(cube, request); return cube; }
/// <summary> /// Pick the color under the specified pixel. /// </summary> /// <param name="texture">The texture</param> /// <param name="pixel">The coordinate of the pixel</param> public unsafe Color PickColor(TexImage texture, Int2 pixel) { if (texture == null) throw new ArgumentNullException(nameof(texture)); var format = texture.Format; if (texture.Dimension != TexImage.TextureDimension.Texture2D || !(format.IsRGBAOrder() || format.IsBGRAOrder() || format.SizeInBytes() != 4)) throw new NotImplementedException(); // check that the pixel is inside the texture var textureRegion = new Rectangle(0, 0, texture.Width, texture.Height); if (!textureRegion.Contains(pixel)) throw new ArgumentException("The provided pixel coordinate is outside of the texture"); var ptr = (uint*)texture.Data; var stride = texture.RowPitch / 4; var pixelColorInt = ptr[stride*pixel.Y + pixel.X]; var pixelColor = format.IsRGBAOrder() ? Color.FromRgba(pixelColorInt) : Color.FromBgra(pixelColorInt); return pixelColor; }
/// <summary> /// Loads the specified request. /// </summary> /// <param name="request">The request.</param> /// <returns>An instance of the class <see cref="TexImage"/> containing your loaded image</returns> /// <exception cref="TextureToolsException">No available library could perform the task : LoadingRequest</exception> private TexImage Load(LoadingRequest request) { var texImage = new TexImage(); texImage.Name = request.FilePath == null ? "" : Path.GetFileName(request.FilePath); foreach (ITexLibrary library in textureLibraries) { if (library.CanHandleRequest(texImage, request)) { library.Execute(texImage, request); texImage.CurrentLibrary = library; return texImage; } } Log.Error("No available library could load your texture : " + request.Type); throw new TextureToolsException("No available library could perform the task : " + request.Type); }
/// <summary> /// Create a new image from region. /// </summary> /// <param name="texImage">The original image from which to extract the region</param> /// <param name="region">The region from the original image to extract.</param> /// <returns>The extracted region <see cref="TexImage"/>. Note: it is the user responsibility to dispose the returned image.</returns> public unsafe Image CreateImageFromRegion(TexImage texImage, Rectangle region) { if (texImage.Dimension != TexImage.TextureDimension.Texture2D || texImage.Format.IsCompressed()) throw new NotImplementedException(); Log.Info("Extracting region and exporting to Paradox Image ..."); // clamp the provided region to be sure it fits in provided image region.X = Math.Max(0, Math.Min(region.X, texImage.Width)); region.Y = Math.Max(0, Math.Min(region.Y, texImage.Height)); region.Width = Math.Max(0, Math.Min(region.Width, texImage.Width - region.X)); region.Height = Math.Max(0, Math.Min(region.Height, texImage.Height - region.Y)); // create the paradox image var pdxImage = Image.New2D(region.Width, region.Height, 1, texImage.Format); if (pdxImage == null) { Log.Error("Image could not be created."); throw new InvalidOperationException("Image could not be created."); } // get the row pitch of the paradox image var pixelBuffer = pdxImage.GetPixelBuffer(0, 0); var dstRowPitch = pixelBuffer.RowStride; // copy the data if (texImage.ArraySize > 0) { var rowSrcPtr = texImage.SubImageArray[0].Data; var rowDstPtr = pdxImage.DataPointer; rowSrcPtr = IntPtr.Add(rowSrcPtr, region.Y * texImage.RowPitch); for (int i = 0; i < region.Height; i++) { var pSrc = ((UInt32*)rowSrcPtr) + region.X; var pDst = (UInt32*)rowDstPtr; for (int x = 0; x < region.Width; x++) *(pDst++) = *(pSrc++); rowSrcPtr = IntPtr.Add(rowSrcPtr, texImage.RowPitch); rowDstPtr = IntPtr.Add(rowDstPtr, dstRowPitch); } } return pdxImage; }
/// <summary> /// Decompresses the specified <see cref="TexImage"/>. /// </summary> /// <param name="image">The <see cref="TexImage"/>.</param> /// <param name="isSRgb">Indicate is the image to decompress is an sRGB image</param> public void Decompress(TexImage image, bool isSRgb) { if (!image.Format.IsCompressed()) { return; } ExecuteRequest(image, new DecompressingRequest(isSRgb, image.Format)); }
/// <summary> /// Corrects the gamma. /// </summary> /// <param name="image">The image.</param> /// <param name="gamma">The gamma.</param> public void CorrectGamma(TexImage image, double gamma) { if (gamma <= 0) { Log.Error("The gamma must be a positive float."); throw new TextureToolsException("The gamma must be a positive float."); } if (image.Format.IsCompressed()) { Log.Warning("You can't correct gamme on a compressed texture. It will be decompressed first.."); Decompress(image, image.Format.IsSRgb()); } var request = new GammaCorrectionRequest(gamma); ExecuteRequest(image, request); }
/// <summary> /// Saves the specified <see cref="TexImage"/> into a file. /// </summary> /// <param name="image">The image.</param> /// <param name="fileName">Name of the file.</param> /// <param name="minimumMipMapSize">Minimum size of the mip map.</param> public void Save(TexImage image, String fileName, int minimumMipMapSize=1) { if (fileName == null || fileName.Equals("")) { Log.Error("No file name entered."); throw new TextureToolsException("No file name entered."); } var request = new ExportRequest(fileName, minimumMipMapSize); if (FindLibrary(image, request) == null && image.Format.IsCompressed()) { Log.Warning("No library can export this texture with the actual compression format. We will try to decompress it first."); Decompress(image, image.Format.IsSRgb()); } ExecuteRequest(image, request); }
/// <summary> /// Swaps two slices of a texture array. /// </summary> /// <param name="image">The image.</param> /// <param name="firstIndex">The index of the first sub-image.</param> /// <param name="secondIndex">The index of the second sub-image</param> public void Swap(TexImage image, int firstIndex, int secondIndex) { var request = new SwappingRequest(firstIndex, secondIndex); ExecuteRequest(image, request); }
/// <summary> /// Saves the specified <see cref="TexImage"/> into a file with the specified format. /// </summary> /// <param name="image">The image.</param> /// <param name="fileName">Name of the file.</param> /// <param name="format">The new format.</param> /// <param name="minimumMipMapSize">Minimum size of the mip map.</param> public void Save(TexImage image, String fileName, PixelFormat format, int minimumMipMapSize = 1) { if (fileName == null || fileName.Equals("")) { Log.Error("No file name entered."); throw new TextureToolsException("No file name entered."); } if (minimumMipMapSize < 0) { Log.Error("The minimup Mipmap size can't be negative. Put 0 or 1 for a complete Mipmap chain."); throw new TextureToolsException("The minimup Mipmap size can't be negative. Put 0 or 1 for a complete Mipmap chain."); } if (image.Format != format && format.IsCompressed() && !image.Format.IsCompressed()) { TexImage workingImage = (TexImage)image.Clone(); Compress(workingImage, format); ExecuteRequest(workingImage, new ExportRequest(fileName, minimumMipMapSize)); workingImage.Dispose(); } else if (image.Format != format && format.IsCompressed()) { TexImage workingImage = (TexImage)image.Clone(); Decompress(workingImage, image.Format.IsSRgb()); Compress(workingImage, format); ExecuteRequest(workingImage, new ExportRequest(fileName, minimumMipMapSize)); workingImage.Dispose(); } else { ExecuteRequest(image, new ExportRequest(fileName, minimumMipMapSize)); } }