/// <summary> /// Converts a DigitalRune <see cref="Texture"/> to an XNA <see cref="TextureContent"/>. /// </summary> /// <param name="texture">The <see cref="Texture"/>.</param> /// <param name="identity">The content identity.</param> /// <returns>The <see cref="TextureContent"/>.</returns> public static TextureContent ToContent(Texture texture, ContentIdentity identity) { var description = texture.Description; switch (description.Dimension) { case TextureDimension.Texture1D: case TextureDimension.Texture2D: { var textureContent = new Texture2DContent { Identity = identity }; for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { var image = texture.Images[texture.GetImageIndex(mipIndex, 0, 0)]; textureContent.Mipmaps.Add(ToContent(image)); } return(textureContent); } case TextureDimension.TextureCube: { var textureContent = new TextureCubeContent { Identity = identity }; for (int faceIndex = 0; faceIndex < 6; faceIndex++) { for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { var image = texture.Images[texture.GetImageIndex(mipIndex, faceIndex, 0)]; textureContent.Faces[faceIndex].Add(ToContent(image)); } } return(textureContent); } case TextureDimension.Texture3D: { var textureContent = new Texture3DContent { Identity = identity }; for (int zIndex = 0; zIndex < description.Depth; zIndex++) { textureContent.Faces.Add(new MipmapChain()); for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { var image = texture.Images[texture.GetImageIndex(mipIndex, 0, zIndex)]; textureContent.Faces[zIndex].Add(ToContent(image)); } } return(textureContent); } } throw new InvalidOperationException("Invalid texture dimension."); }
/// <summary> /// Converts a DigitalRune <see cref="Texture"/> to an XNA <see cref="TextureContent"/>. /// </summary> /// <param name="texture">The <see cref="Texture"/>.</param> /// <param name="identity">The content identity.</param> /// <returns>The <see cref="TextureContent"/>.</returns> public static TextureContent ToContent(Texture texture, ContentIdentity identity) { var description = texture.Description; switch (description.Dimension) { case TextureDimension.Texture1D: case TextureDimension.Texture2D: { var textureContent = new Texture2DContent { Identity = identity }; for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { var image = texture.Images[texture.GetImageIndex(mipIndex, 0, 0)]; textureContent.Mipmaps.Add(ToContent(image)); } return textureContent; } case TextureDimension.TextureCube: { var textureContent = new TextureCubeContent { Identity = identity }; for (int faceIndex = 0; faceIndex < 6; faceIndex++) { for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { var image = texture.Images[texture.GetImageIndex(mipIndex, faceIndex, 0)]; textureContent.Faces[faceIndex].Add(ToContent(image)); } } return textureContent; } case TextureDimension.Texture3D: { var textureContent = new Texture3DContent { Identity = identity }; for (int zIndex = 0; zIndex < description.Depth; zIndex++) { textureContent.Faces.Add(new MipmapChain()); for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { var image = texture.Images[texture.GetImageIndex(mipIndex, 0, zIndex)]; textureContent.Faces[zIndex].Add(ToContent(image)); } } return textureContent; } } throw new InvalidOperationException("Invalid texture dimension."); }
private static void Resize(Texture srcTexture, int srcMipIndex, int srcArrayOrFaceIndex, Texture dstTexture, int dstMipIndex, int dstArrayOrFaceIndex, Filter filter, bool alphaTransparency, TextureAddressMode wrapMode) { if (srcTexture == null) throw new ArgumentNullException("srcTexture"); if (dstTexture == null) throw new ArgumentNullException("dstTexture"); if (srcTexture == dstTexture && srcMipIndex == dstMipIndex) return; int srcDepth = srcTexture.GetDepth(srcMipIndex); int dstDepth = dstTexture.GetDepth(dstMipIndex); if (srcDepth == dstDepth) { // Resize 2D. int srcIndex = srcTexture.GetImageIndex(srcMipIndex, srcArrayOrFaceIndex, 0); int srcWidth = srcTexture.Images[srcIndex].Width; int srcHeight = srcTexture.Images[srcIndex].Height; int dstIndex = dstTexture.GetImageIndex(dstMipIndex, dstArrayOrFaceIndex, 0); int dstWidth = dstTexture.Images[dstIndex].Width; int dstHeight = dstTexture.Images[dstIndex].Height; var kernelX = new PolyphaseKernel(filter, srcWidth, dstWidth, 32); var kernelY = new PolyphaseKernel(filter, srcHeight, dstHeight, 32); #if SINGLE_THREADED for (int z = 0; z < srcDepth; z++) #else Parallel.For(0, srcDepth, z => #endif { var srcImage = srcTexture.Images[srcTexture.GetImageIndex(srcMipIndex, srcArrayOrFaceIndex, z)]; var dstImage = dstTexture.Images[dstTexture.GetImageIndex(dstMipIndex, dstArrayOrFaceIndex, z)]; Resize2D(srcImage, dstImage, alphaTransparency, wrapMode, kernelX, kernelY); } #if !SINGLE_THREADED ); #endif } else { // Resize 3D. Resize3D(srcTexture, srcMipIndex, srcArrayOrFaceIndex, dstTexture, dstMipIndex, dstArrayOrFaceIndex, filter, alphaTransparency, wrapMode); } }
public void BeginImage(int size, int width, int height, int depth, int face, int miplevel) { _imageIndex = _texture.GetImageIndex(miplevel, face, 0); _dataOffset = 0; Debug.Assert(size == _texture.Images[_imageIndex].Data.Length); Debug.Assert(width == _texture.Images[_imageIndex].Width); Debug.Assert(height == _texture.Images[_imageIndex].Height); Debug.Assert(depth == 1, "Volume texture are not yet supported in NVTT."); }
/// <summary> /// Initializes a new instance of the <see cref="VolumeAccessor"/> struct. /// </summary> /// <param name="texture">The volume texture.</param> /// <param name="mipIndex">The mipmap level.</param> /// <param name="arrayOrFaceIndex"> /// The array index for texture arrays, or the face index for cube maps. Must be 0 for volume /// textures. /// </param> public VolumeAccessor(Texture texture, int mipIndex, int arrayOrFaceIndex) { if (texture == null) { throw new ArgumentNullException("texture"); } int index = texture.GetImageIndex(mipIndex, arrayOrFaceIndex, 0); var image = texture.Images[index]; _width = image.Width; _height = image.Height; _depth = texture.Description.Depth; _handles = new GCHandle[_depth]; _intPtrs = new IntPtr[_depth]; for (int z = 0; z < _depth; z++) { image = texture.Images[index + z]; _handles[z] = GCHandle.Alloc(image.Data, GCHandleType.Pinned); _intPtrs[z] = _handles[z].AddrOfPinnedObject(); } }
//-------------------------------------------------------------- #region XNA Content Pipeline //-------------------------------------------------------------- /// <summary> /// Converts an XNA <see cref="TextureContent"/> to a DigitalRune <see cref="Texture"/>. /// </summary> /// <param name="textureContent">The <see cref="TextureContent"/>.</param> /// <returns>The <see cref="Texture"/>.</returns> public static Texture ToTexture(TextureContent textureContent) { SurfaceFormat surfaceFormat; var bitmapContent0 = textureContent.Faces[0][0]; if (!bitmapContent0.TryGetFormat(out surfaceFormat)) { throw new InvalidContentException("Invalid surface format.", textureContent.Identity); } var texture2DContent = textureContent as Texture2DContent; if (texture2DContent != null) { var description = new TextureDescription { Dimension = TextureDimension.Texture2D, Width = bitmapContent0.Width, Height = bitmapContent0.Height, Depth = 1, MipLevels = texture2DContent.Mipmaps.Count, ArraySize = 1, Format = surfaceFormat.ToDataFormat() }; var texture = new Texture(description); for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { var bitmapContent = texture2DContent.Mipmaps[mipIndex]; var image = texture.Images[texture.GetImageIndex(mipIndex, 0, 0)]; Buffer.BlockCopy(bitmapContent.GetPixelData(), 0, image.Data, 0, image.Data.Length); } return(texture); } var textureCubeContent = textureContent as TextureCubeContent; if (textureCubeContent != null) { var description = new TextureDescription { Dimension = TextureDimension.TextureCube, Width = bitmapContent0.Width, Height = bitmapContent0.Height, Depth = 1, MipLevels = textureCubeContent.Faces[0].Count, ArraySize = 6, Format = surfaceFormat.ToDataFormat() }; var texture = new Texture(description); for (int faceIndex = 0; faceIndex < 6; faceIndex++) { for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { var bitmapContent = textureCubeContent.Faces[faceIndex][mipIndex]; var image = texture.Images[texture.GetImageIndex(mipIndex, faceIndex, 0)]; Buffer.BlockCopy(bitmapContent.GetPixelData(), 0, image.Data, 0, image.Data.Length); } } return(texture); } var texture3DContent = textureContent as Texture3DContent; if (texture3DContent != null) { var description = new TextureDescription { Dimension = TextureDimension.Texture3D, Width = bitmapContent0.Width, Height = bitmapContent0.Height, Depth = texture3DContent.Faces.Count, MipLevels = texture3DContent.Faces[0].Count, ArraySize = 1, Format = surfaceFormat.ToDataFormat() }; var texture = new Texture(description); for (int zIndex = 0; zIndex < description.Depth; zIndex++) { for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { var bitmapContent = texture3DContent.Faces[zIndex][mipIndex]; var image = texture.Images[texture.GetImageIndex(mipIndex, 0, zIndex)]; Buffer.BlockCopy(bitmapContent.GetPixelData(), 0, image.Data, 0, image.Data.Length); } } return(texture); } throw new InvalidOperationException("Invalid texture dimension."); }
private static void Resize3D(Texture srcTexture, int srcMipIndex, int srcArrayOrFaceIndex, Texture dstTexture, int dstMipIndex, int dstArrayOrFaceIndex, Filter filter, bool alphaTransparency, TextureAddressMode wrapMode) { int srcIndex = srcTexture.GetImageIndex(srcMipIndex, srcArrayOrFaceIndex, 0); int srcWidth = srcTexture.Images[srcIndex].Width; int srcHeight = srcTexture.Images[srcIndex].Height; int srcDepth = srcTexture.GetDepth(srcMipIndex); int dstIndex = dstTexture.GetImageIndex(dstMipIndex, dstArrayOrFaceIndex, 0); int dstWidth = dstTexture.Images[dstIndex].Width; int dstHeight = dstTexture.Images[dstIndex].Height; int dstDepth = dstTexture.GetDepth(dstMipIndex); // Resize volume. var kernelX = new PolyphaseKernel(filter, srcWidth, dstWidth, 32); var kernelY = new PolyphaseKernel(filter, srcHeight, dstHeight, 32); var kernelZ = new PolyphaseKernel(filter, srcDepth, dstDepth, 32); var tmpTexture = new Texture(new TextureDescription { Dimension = TextureDimension.Texture3D, Width = dstWidth, Height = srcHeight, Depth = srcDepth, MipLevels = 1, ArraySize = 1, Format = DataFormat.R32G32B32A32_FLOAT }); var tmpTexture2 = new Texture(new TextureDescription { Dimension = TextureDimension.Texture3D, Width = dstWidth, Height = dstHeight, Depth = srcDepth, MipLevels = 1, ArraySize = 1, Format = DataFormat.R32G32B32A32_FLOAT }); // ReSharper disable AccessToDisposedClosure using (var srcVolume = new VolumeAccessor(srcTexture, srcMipIndex, srcArrayOrFaceIndex)) using (var tmpVolume = new VolumeAccessor(tmpTexture, 0, 0)) using (var tmpVolume2 = new VolumeAccessor(tmpTexture2, 0, 0)) using (var dstVolume = new VolumeAccessor(dstTexture, dstMipIndex, dstArrayOrFaceIndex)) { // Resize horizontally: srcVolume --> tmpVolume { float scale = (float)tmpVolume.Width / srcVolume.Width; float inverseScale = 1.0f / scale; #if SINGLE_THREADED for (int z = 0; z < tmpVolume.Depth; z++) #else Parallel.For(0, tmpVolume.Depth, z => #endif { for (int y = 0; y < tmpVolume.Height; y++) { // Apply polyphase kernel horizontally. for (int x = 0; x < tmpVolume.Width; x++) { float center = (x + 0.5f) * inverseScale; int left = (int)Math.Floor(center - kernelX.Width); int right = (int)Math.Ceiling(center + kernelX.Width); Debug.Assert(right - left <= kernelX.WindowSize); float totalRgbWeights = 0.0f; Vector4F sum = new Vector4F(); for (int i = 0; i < kernelX.WindowSize; i++) { Vector4F color = srcVolume.GetPixel(left + i, y, z, wrapMode); const float alphaEpsilon = 1.0f / 256.0f; float alpha = alphaTransparency ? color.W + alphaEpsilon : 1.0f; float weight = kernelX.Weights[x, i]; float rgbWeight = weight * alpha; totalRgbWeights += rgbWeight; sum.X += color.X * rgbWeight; sum.Y += color.Y * rgbWeight; sum.Z += color.Z * rgbWeight; sum.W += color.W * weight; } float f = 1 / totalRgbWeights; sum.X *= f; sum.Y *= f; sum.Z *= f; tmpVolume.SetPixel(x, y, z, sum); } } } #if !SINGLE_THREADED ); #endif } // Resize vertically: tmpVolume --> tmpVolume2 { float scale = (float)tmpVolume2.Height / tmpVolume.Height; float inverseScale = 1.0f / scale; #if SINGLE_THREADED for (int z = 0; z < tmpVolume2.Depth; z++) #else Parallel.For(0, tmpVolume2.Depth, z => #endif { for (int x = 0; x < tmpVolume2.Width; x++) { // Apply polyphase kernel vertically. for (int y = 0; y < tmpVolume2.Height; y++) { float center = (y + 0.5f) * inverseScale; int left = (int)Math.Floor(center - kernelY.Width); int right = (int)Math.Ceiling(center + kernelY.Width); Debug.Assert(right - left <= kernelY.WindowSize); float totalRgbWeights = 0.0f; Vector4F sum = new Vector4F(); for (int i = 0; i < kernelY.WindowSize; i++) { Vector4F color = tmpVolume.GetPixel(x, left + i, z, wrapMode); const float alphaEpsilon = 1.0f / 256.0f; float alpha = alphaTransparency ? color.W + alphaEpsilon : 1.0f; float weight = kernelY.Weights[y, i]; float rgbWeight = weight * alpha; totalRgbWeights += rgbWeight; sum.X += color.X * rgbWeight; sum.Y += color.Y * rgbWeight; sum.Z += color.Z * rgbWeight; sum.W += color.W * weight; } float f = 1 / totalRgbWeights; sum.X *= f; sum.Y *= f; sum.Z *= f; tmpVolume2.SetPixel(x, y, z, sum); } } } #if !SINGLE_THREADED ); #endif } // Resize depth: tmpVolume2 --> dstVolume { float scale = (float)dstVolume.Depth / tmpVolume2.Depth; float inverseScale = 1.0f / scale; #if SINGLE_THREADED for (int y = 0; y < dstVolume.Height; y++) #else Parallel.For(0, dstVolume.Height, y => #endif { for (int x = 0; x < dstVolume.Width; x++) { // Apply polyphase kernel along z direction. for (int z = 0; z < dstVolume.Depth; z++) { float center = (z + 0.5f) * inverseScale; int left = (int)Math.Floor(center - kernelZ.Width); int right = (int)Math.Ceiling(center + kernelZ.Width); Debug.Assert(right - left <= kernelZ.WindowSize); float totalRgbWeights = 0.0f; Vector4F sum = new Vector4F(); for (int i = 0; i < kernelZ.WindowSize; i++) { Vector4F color = tmpVolume2.GetPixel(x, y, left + i, wrapMode); const float alphaEpsilon = 1.0f / 256.0f; float alpha = alphaTransparency ? color.W + alphaEpsilon : 1.0f; float weight = kernelZ.Weights[z, i]; float rgbWeight = weight * alpha; totalRgbWeights += rgbWeight; sum.X += color.X * rgbWeight; sum.Y += color.Y * rgbWeight; sum.Z += color.Z * rgbWeight; sum.W += color.W * weight; } float f = 1 / totalRgbWeights; sum.X *= f; sum.Y *= f; sum.Z *= f; dstVolume.SetPixel(x, y, z, sum); } } } #if !SINGLE_THREADED ); #endif } } // ReSharper restore AccessToDisposedClosure }
private static void Resize(Texture srcTexture, int srcMipIndex, int srcArrayOrFaceIndex, Texture dstTexture, int dstMipIndex, int dstArrayOrFaceIndex, Filter filter, bool alphaTransparency, TextureAddressMode wrapMode) { if (srcTexture == null) throw new ArgumentNullException("srcTexture"); if (dstTexture == null) throw new ArgumentNullException("dstTexture"); if (srcTexture == dstTexture && srcMipIndex == dstMipIndex) return; int srcDepth = srcTexture.GetDepth(srcMipIndex); int dstDepth = dstTexture.GetDepth(dstMipIndex); if (srcDepth == dstDepth) { // Resize 2D. int srcIndex = srcTexture.GetImageIndex(srcMipIndex, srcArrayOrFaceIndex, 0); int srcWidth = srcTexture.Images[srcIndex].Width; int srcHeight = srcTexture.Images[srcIndex].Height; int dstIndex = dstTexture.GetImageIndex(dstMipIndex, dstArrayOrFaceIndex, 0); int dstWidth = dstTexture.Images[dstIndex].Width; int dstHeight = dstTexture.Images[dstIndex].Height; var kernelX = new PolyphaseKernel(filter, srcWidth, dstWidth, 32); var kernelY = new PolyphaseKernel(filter, srcHeight, dstHeight, 32); #if SINGLE_THREADED for (int z = 0; z < srcDepth; z++) #else Parallel.For(0, srcDepth, z => #endif { var srcImage = srcTexture.Images[srcTexture.GetImageIndex(srcMipIndex, srcArrayOrFaceIndex, z)]; var dstImage = dstTexture.Images[dstTexture.GetImageIndex(dstMipIndex, dstArrayOrFaceIndex, z)]; Resize2D(srcImage, dstImage, alphaTransparency, wrapMode, kernelX, kernelY); } #if !SINGLE_THREADED ); #endif } else { // Resize 3D. Resize3D(srcTexture, srcMipIndex, srcArrayOrFaceIndex, dstTexture, dstMipIndex, dstArrayOrFaceIndex, filter, alphaTransparency, wrapMode); } }
/// <summary> /// Resizes the specified texture and/or generates mipmaps. /// </summary> /// <param name="texture">The texture.</param> /// <param name="width">The desired width.</param> /// <param name="height">The desired height.</param> /// <param name="inputGamma">The input gamma.</param> /// <param name="outputGamma">The output gamma.</param> /// <param name="generateMipmaps"> /// <see langword="true"/> to generate all mipmap levels; otherwise <see langword="false"/>. /// </param> /// <param name="hasAlpha"> /// <see langword="true"/> if <paramref name="texture"/> requires an alpha channel; otherwise, /// <see langword="false"/> if <paramref name="texture"/> is opaque. /// </param> /// <param name="hasFractionalAlpha"> /// <see langword="true"/> if <paramref name="texture"/> contains fractional alpha values; /// otherwise, <see langword="false"/> if <paramref name="texture"/> is opaque or contains only /// binary alpha. /// </param> /// <param name="premultipliedAlpha"> /// <see langword="true"/> when <paramref name="texture"/> is using premultiplied alpha.; /// otherwise, <see langword="false"/>.</param> /// <returns>The resized texture.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="texture"/> is <see langword="null"/>. /// </exception> public static Texture ResizeAndGenerateMipmaps(Texture texture, int width, int height, float inputGamma, float outputGamma, bool generateMipmaps, bool hasAlpha, bool hasFractionalAlpha, bool premultipliedAlpha) { if (texture == null) throw new ArgumentNullException("texture"); // NVIDIA Texture Tools expect BGRA 8:8:8:8. if (texture.Description.Format != TextureFormat.B8G8R8A8_UNorm) throw new ArgumentException("Texture format needs to be B8G8R8A8_UNORM.", "texture"); if (texture.Description.Dimension != TextureDimension.TextureCube && texture.Description.ArraySize > 1) throw new NotSupportedException("Resizing and mipmap generation for texture arrays is not supported."); if (texture.Description.Dimension == TextureDimension.Texture3D) throw new NotSupportedException("Resizing and mipmap generation for volume textures is not supported."); // ----- InputOptions var inputOptions = new InputOptions(); inputOptions.SetAlphaMode(hasAlpha ? (premultipliedAlpha ? AlphaMode.Premultiplied : AlphaMode.Transparency) : AlphaMode.None); inputOptions.SetFormat(InputFormat.BGRA_8UB); inputOptions.SetGamma(inputGamma, outputGamma); inputOptions.SetMipmapFilter(MipmapFilter.Box); inputOptions.SetMipmapGeneration(generateMipmaps); bool roundToPowerOfTwo = (width != texture.Description.Width || height != texture.Description.Height); inputOptions.SetRoundMode(roundToPowerOfTwo ? RoundMode.ToNextPowerOfTwo : RoundMode.None); inputOptions.SetWrapMode(WrapMode.Mirror); var description = texture.Description; bool isCube = description.Dimension == TextureDimension.TextureCube; var textureType = isCube ? TextureType.TextureCube : TextureType.Texture2D; inputOptions.SetTextureLayout(textureType, description.Width, description.Height, 1); for (int arrayIndex = 0; arrayIndex < description.ArraySize; arrayIndex++) { for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { int index = texture.GetImageIndex(mipIndex, arrayIndex, 0); var image = texture.Images[index]; var handle = GCHandle.Alloc(image.Data, GCHandleType.Pinned); inputOptions.SetMipmapData(handle.AddrOfPinnedObject(), image.Width, image.Height, 1, arrayIndex, mipIndex); handle.Free(); } } // ----- OutputOptions var outputOptions = new OutputOptions(); outputOptions.SetOutputHeader(false); outputOptions.Error += OnError; description.Format = TextureFormat.R8G8B8A8_UNorm; description.Width = width; description.Height = height; description.MipLevels = generateMipmaps ? CalculateMipLevels(width, height) : 1; var resizedTexture = new Texture(description); var outputHandler = new OutputHandler(resizedTexture); outputOptions.SetOutputHandler(outputHandler.BeginImage, outputHandler.WriteData); // ----- CompressionOptions var compressionOptions = new CompressionOptions(); compressionOptions.SetFormat(Format.RGBA); compressionOptions.SetPixelFormat(32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000); compressionOptions.SetQuality(Quality.Normal); // ----- Run NVTT try { var compressor = new Compressor(); compressor.Compress(inputOptions, compressionOptions, outputOptions); } catch (NullReferenceException) { // Resizing and mipmap generation without compression sometimes causes a // NullReferenceException in nvttCompress(). throw new Exception("NullReferenceException in NVIDIA texture tools. Please try again."); } return resizedTexture; }
/// <overloads> /// <summary> /// Scales the alpha values to create an equal alpha test coverage across all mipmap levels. /// </summary> /// </overloads> /// /// <summary> /// Scales the alpha values to create an equal alpha test coverage across all mipmap levels. /// </summary> /// <param name="texture">The texture (<see cref="DataFormat.R32G32B32A32_FLOAT"/>).</param> /// <param name="referenceAlpha">The reference alpha.</param> /// <param name="premultipliedAlpha"> /// <see langword="true"/> if texture uses premultiplied alpha. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="texture"/> is <see langword="null"/>. /// </exception> public static void ScaleAlphaToCoverage(Texture texture, float referenceAlpha, bool premultipliedAlpha) { // See http://the-witness.net/news/2010/09/computing-alpha-mipmaps/. // Reference implementation: // NVIDIA Texture Tools - http://code.google.com/p/nvidia-texture-tools/) // file nvidia-texture-tools\src\nvimage\FloatImage.cpp, method scaleAlphaToCoverage() // (Original code marked as "public domain".) if (texture == null) throw new ArgumentNullException("texture"); if (texture.Description.Dimension == TextureDimension.Texture3D) throw new ArgumentException("Scaling alpha-to-coverage is not supported for volume textures."); int mipLevels = texture.Description.MipLevels; int arraySize = texture.Description.ArraySize; #if SINGLE_THREADED for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) #else Parallel.For(0, arraySize, arrayIndex => #endif { int index0 = texture.GetImageIndex(0, arrayIndex, 0); float coverage = GetAlphaTestCoverage(texture.Images[index0], referenceAlpha); #if SINGLE_THREADED for (int mipIndex = 0; mipIndex < mipLevels; mipIndex++) #else Parallel.For(1, mipLevels, mipIndex => #endif { int index = texture.GetImageIndex(mipIndex, arrayIndex, 0); ScaleAlphaToCoverage(texture.Images[index], referenceAlpha, coverage, premultipliedAlpha); } #if !SINGLE_THREADED ); #endif } #if !SINGLE_THREADED ); #endif }
/// <overloads> /// <summary> /// Determines whether the specified texture/image uses the alpha channel. /// </summary> /// </overloads> /// /// <summary> /// Determines whether the specified texture uses the alpha channel. /// </summary> /// <param name="texture">The texture.</param> /// <param name="hasAlpha"> /// <see langword="true"/> if <paramref name="texture"/> requires an alpha channel; otherwise, /// <see langword="false"/> if <paramref name="texture"/> is opaque. /// </param> /// <param name="hasFractionalAlpha"> /// <see langword="true"/> if <paramref name="texture"/> contains fractional alpha values; /// otherwise, <see langword="false"/> if <paramref name="texture"/> is opaque or contains only /// binary alpha. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="texture"/> is <see langword="null"/>. /// </exception> public static void HasAlpha(Texture texture, out bool hasAlpha, out bool hasFractionalAlpha) { if (texture == null) throw new ArgumentNullException("texture"); // Test mip level 0. Image[] images; switch (texture.Description.Dimension) { case TextureDimension.Texture1D: case TextureDimension.Texture2D: case TextureDimension.TextureCube: { images = new Image[texture.Description.ArraySize]; for (int arrayIndex = 0; arrayIndex < images.Length; arrayIndex++) images[arrayIndex] = texture.Images[texture.GetImageIndex(0, arrayIndex, 0)]; } break; case TextureDimension.Texture3D: { images = new Image[texture.Description.Depth]; for (int zIndex = 0; zIndex < images.Length; zIndex++) images[zIndex] = texture.Images[texture.GetImageIndex(0, 0, zIndex)]; } break; default: throw new NotSupportedException("The specified texture dimension is not supported."); } hasAlpha = false; hasFractionalAlpha = false; foreach (var image in images) { bool currentHasAlpha, currentHasFractionalAlpha; HasAlpha(image, out currentHasAlpha, out currentHasFractionalAlpha); hasAlpha = hasAlpha || currentHasAlpha; hasFractionalAlpha = currentHasFractionalAlpha; if (hasFractionalAlpha) return; } }
/// <summary> /// Mirrors the volume texture along the z-axis. /// </summary> /// <param name="texture">The texture.</param> /// <remarks> /// The method does nothing if <paramref name="texture"/> is not a volume texture. /// </remarks> /// <exception cref="ArgumentNullException"> /// <paramref name="texture"/> is <see langword="null"/>. /// </exception> public static void FlipZ(Texture texture) { if (texture == null) throw new ArgumentNullException("texture"); var description = texture.Description; if (description.Dimension != TextureDimension.Texture3D) return; for (int level = 0; level < description.MipLevels; level++) { int baseIndex = texture.GetImageIndex(level, 0, 0); for (int z0 = 0; z0 < description.Depth / 2; z0++) { int z1 = description.Depth - z0 - 1; int index0 = baseIndex + z0; int index1 = baseIndex + z1; var image0 = texture.Images[index0]; var image1 = texture.Images[index1]; texture.Images[index0] = image1; texture.Images[index1] = image0; } } }
/// <summary> /// Resizes the specified texture and/or generates mipmaps. /// </summary> /// <param name="texture">The texture.</param> /// <param name="width">The desired width.</param> /// <param name="height">The desired height.</param> /// <param name="inputGamma">The input gamma.</param> /// <param name="outputGamma">The output gamma.</param> /// <param name="generateMipmaps"> /// <see langword="true"/> to generate all mipmap levels; otherwise <see langword="false"/>. /// </param> /// <param name="hasAlpha"> /// <see langword="true"/> if <paramref name="texture"/> requires an alpha channel; otherwise, /// <see langword="false"/> if <paramref name="texture"/> is opaque. /// </param> /// <param name="hasFractionalAlpha"> /// <see langword="true"/> if <paramref name="texture"/> contains fractional alpha values; /// otherwise, <see langword="false"/> if <paramref name="texture"/> is opaque or contains only /// binary alpha. /// </param> /// <param name="premultipliedAlpha"> /// <see langword="true"/> when <paramref name="texture"/> is using premultiplied alpha.; /// otherwise, <see langword="false"/>.</param> /// <returns>The resized texture.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="texture"/> is <see langword="null"/>. /// </exception> public static Texture ResizeAndGenerateMipmaps(Texture texture, int width, int height, float inputGamma, float outputGamma, bool generateMipmaps, bool hasAlpha, bool hasFractionalAlpha, bool premultipliedAlpha) { if (texture == null) { throw new ArgumentNullException("texture"); } // NVIDIA Texture Tools expect BGRA 8:8:8:8. if (texture.Description.Format != TextureFormat.B8G8R8A8_UNorm) { throw new ArgumentException("Texture format needs to be B8G8R8A8_UNORM.", "texture"); } if (texture.Description.Dimension != TextureDimension.TextureCube && texture.Description.ArraySize > 1) { throw new NotSupportedException("Resizing and mipmap generation for texture arrays is not supported."); } if (texture.Description.Dimension == TextureDimension.Texture3D) { throw new NotSupportedException("Resizing and mipmap generation for volume textures is not supported."); } // ----- InputOptions var inputOptions = new InputOptions(); inputOptions.SetAlphaMode(hasAlpha ? (premultipliedAlpha ? AlphaMode.Premultiplied : AlphaMode.Transparency) : AlphaMode.None); inputOptions.SetFormat(InputFormat.BGRA_8UB); inputOptions.SetGamma(inputGamma, outputGamma); inputOptions.SetMipmapFilter(MipmapFilter.Box); inputOptions.SetMipmapGeneration(generateMipmaps); bool roundToPowerOfTwo = (width != texture.Description.Width || height != texture.Description.Height); inputOptions.SetRoundMode(roundToPowerOfTwo ? RoundMode.ToNextPowerOfTwo : RoundMode.None); inputOptions.SetWrapMode(WrapMode.Mirror); var description = texture.Description; bool isCube = description.Dimension == TextureDimension.TextureCube; var textureType = isCube ? TextureType.TextureCube : TextureType.Texture2D; inputOptions.SetTextureLayout(textureType, description.Width, description.Height, 1); for (int arrayIndex = 0; arrayIndex < description.ArraySize; arrayIndex++) { for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { int index = texture.GetImageIndex(mipIndex, arrayIndex, 0); var image = texture.Images[index]; var handle = GCHandle.Alloc(image.Data, GCHandleType.Pinned); inputOptions.SetMipmapData(handle.AddrOfPinnedObject(), image.Width, image.Height, 1, arrayIndex, mipIndex); handle.Free(); } } // ----- OutputOptions var outputOptions = new OutputOptions(); outputOptions.SetOutputHeader(false); outputOptions.Error += OnError; description.Format = TextureFormat.R8G8B8A8_UNorm; description.Width = width; description.Height = height; description.MipLevels = generateMipmaps ? CalculateMipLevels(width, height) : 1; var resizedTexture = new Texture(description); var outputHandler = new OutputHandler(resizedTexture); outputOptions.SetOutputHandler(outputHandler.BeginImage, outputHandler.WriteData); // ----- CompressionOptions var compressionOptions = new CompressionOptions(); compressionOptions.SetFormat(Format.RGBA); compressionOptions.SetPixelFormat(32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000); compressionOptions.SetQuality(Quality.Normal); // ----- Run NVTT try { var compressor = new Compressor(); compressor.Compress(inputOptions, compressionOptions, outputOptions); } catch (NullReferenceException) { // Resizing and mipmap generation without compression sometimes causes a // NullReferenceException in nvttCompress(). throw new Exception("NullReferenceException in NVIDIA texture tools. Please try again."); } return(resizedTexture); }
/// <summary> /// Compresses the specified texture using a Block Compression format (BC<i>n</i>). /// </summary> /// <param name="texture">The uncompressed texture.</param> /// <param name="inputGamma">The input gamma.</param> /// <param name="outputGamma">The output gamma.</param> /// <param name="generateMipmaps"> /// <see langword="true"/> to generate all mipmap levels; otherwise <see langword="false"/>. /// </param> /// <param name="hasAlpha"> /// <see langword="true"/> if <paramref name="texture"/> requires an alpha channel; otherwise, /// <see langword="false"/> if <paramref name="texture"/> is opaque. /// </param> /// <param name="hasFractionalAlpha"> /// <see langword="true"/> if <paramref name="texture"/> contains fractional alpha values; /// otherwise, <see langword="false"/> if <paramref name="texture"/> is opaque or contains only /// binary alpha. /// </param> /// <param name="premultipliedAlpha"> /// <see langword="true"/> when <paramref name="texture"/> is using premultiplied alpha.; /// otherwise, <see langword="false"/>.</param> /// <param name="sharpAlpha"> /// <see langword="true"/> when the texture contains a sharp alpha mask; otherwise /// <see langword="false"/>. /// </param> /// <returns>The compressed texture.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="texture"/> is <see langword="null"/>. /// </exception> /// <exception cref="ArgumentException"> /// Texture width and height need to be equal (square texture) and a power of two (POT /// texture). /// </exception> internal static Texture CompressBCn(Texture texture, float inputGamma, float outputGamma, bool generateMipmaps, bool hasAlpha, bool hasFractionalAlpha, bool premultipliedAlpha, bool sharpAlpha = false) { if (texture == null) { throw new ArgumentNullException("texture"); } if (texture.Description.Dimension == TextureDimension.Texture3D) { throw new NotSupportedException("Texture compression for volume textures is not supported."); } // NVIDIA Texture Tools expect BGRA 8:8:8:8. texture = texture.ConvertTo(TextureFormat.B8G8R8A8_UNorm); // ----- InputOptions var inputOptions = new InputOptions(); inputOptions.SetAlphaMode(hasAlpha ? (premultipliedAlpha ? AlphaMode.Premultiplied : AlphaMode.Transparency) : AlphaMode.None); inputOptions.SetFormat(InputFormat.BGRA_8UB); inputOptions.SetGamma(inputGamma, outputGamma); inputOptions.SetMipmapFilter(MipmapFilter.Box); inputOptions.SetMipmapGeneration(generateMipmaps); inputOptions.SetRoundMode(RoundMode.None); // Size is set explicitly. inputOptions.SetWrapMode(WrapMode.Mirror); var description = texture.Description; bool isCube = description.Dimension == TextureDimension.TextureCube; var textureType = isCube ? TextureType.TextureCube : TextureType.Texture2D; inputOptions.SetTextureLayout(textureType, description.Width, description.Height, 1); for (int arrayIndex = 0; arrayIndex < description.ArraySize; arrayIndex++) { for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { int index = texture.GetImageIndex(mipIndex, arrayIndex, 0); var image = texture.Images[index]; var handle = GCHandle.Alloc(image.Data, GCHandleType.Pinned); inputOptions.SetMipmapData(handle.AddrOfPinnedObject(), image.Width, image.Height, 1, arrayIndex, mipIndex); handle.Free(); } } // ----- OutputOptions var outputOptions = new OutputOptions(); outputOptions.SetOutputHeader(false); outputOptions.Error += OnError; Format compressedFormat; if (hasAlpha) { if (sharpAlpha) { compressedFormat = Format.BC2; description.Format = TextureFormat.BC2_UNorm; } else { compressedFormat = Format.BC3; description.Format = TextureFormat.BC3_UNorm; } } else { compressedFormat = Format.BC1; description.Format = TextureFormat.BC1_UNorm; } var compressedTexture = new Texture(description); var outputHandler = new OutputHandler(compressedTexture); outputOptions.SetOutputHandler(outputHandler.BeginImage, outputHandler.WriteData); // ----- CompressionOptions var compressionOptions = new CompressionOptions(); compressionOptions.SetFormat(compressedFormat); compressionOptions.SetQuality(Quality.Normal); // ----- Run NVTT try { var compressor = new Compressor(); compressor.Compress(inputOptions, compressionOptions, outputOptions); } catch (NullReferenceException) { throw new Exception("NullReferenceException in NVIDIA texture tools. Please try again."); } return(compressedTexture); }
/// <summary> /// Initializes a new instance of the <see cref="VolumeAccessor"/> struct. /// </summary> /// <param name="texture">The volume texture.</param> /// <param name="mipIndex">The mipmap level.</param> /// <param name="arrayOrFaceIndex"> /// The array index for texture arrays, or the face index for cube maps. Must be 0 for volume /// textures. /// </param> public VolumeAccessor(Texture texture, int mipIndex, int arrayOrFaceIndex) { if (texture == null) throw new ArgumentNullException("texture"); int index = texture.GetImageIndex(mipIndex, arrayOrFaceIndex, 0); var image = texture.Images[index]; _width = image.Width; _height = image.Height; _depth = texture.Description.Depth; _handles = new GCHandle[_depth]; _intPtrs = new IntPtr[_depth]; for (int z = 0; z < _depth; z++) { image = texture.Images[index + z]; _handles[z] = GCHandle.Alloc(image.Data, GCHandleType.Pinned); _intPtrs[z] = _handles[z].AddrOfPinnedObject(); } }
private static void Resize3D(Texture srcTexture, int srcMipIndex, int srcArrayOrFaceIndex, Texture dstTexture, int dstMipIndex, int dstArrayOrFaceIndex, Filter filter, bool alphaTransparency, TextureAddressMode wrapMode) { int srcIndex = srcTexture.GetImageIndex(srcMipIndex, srcArrayOrFaceIndex, 0); int srcWidth = srcTexture.Images[srcIndex].Width; int srcHeight = srcTexture.Images[srcIndex].Height; int srcDepth = srcTexture.GetDepth(srcMipIndex); int dstIndex = dstTexture.GetImageIndex(dstMipIndex, dstArrayOrFaceIndex, 0); int dstWidth = dstTexture.Images[dstIndex].Width; int dstHeight = dstTexture.Images[dstIndex].Height; int dstDepth = dstTexture.GetDepth(dstMipIndex); // Resize volume. var kernelX = new PolyphaseKernel(filter, srcWidth, dstWidth, 32); var kernelY = new PolyphaseKernel(filter, srcHeight, dstHeight, 32); var kernelZ = new PolyphaseKernel(filter, srcDepth, dstDepth, 32); var tmpTexture = new Texture(new TextureDescription { Dimension = TextureDimension.Texture3D, Width = dstWidth, Height = srcHeight, Depth = srcDepth, MipLevels = 1, ArraySize = 1, Format = DataFormat.R32G32B32A32_FLOAT }); var tmpTexture2 = new Texture(new TextureDescription { Dimension = TextureDimension.Texture3D, Width = dstWidth, Height = dstHeight, Depth = srcDepth, MipLevels = 1, ArraySize = 1, Format = DataFormat.R32G32B32A32_FLOAT }); // ReSharper disable AccessToDisposedClosure using (var srcVolume = new VolumeAccessor(srcTexture, srcMipIndex, srcArrayOrFaceIndex)) using (var tmpVolume = new VolumeAccessor(tmpTexture, 0, 0)) using (var tmpVolume2 = new VolumeAccessor(tmpTexture2, 0, 0)) using (var dstVolume = new VolumeAccessor(dstTexture, dstMipIndex, dstArrayOrFaceIndex)) { // Resize horizontally: srcVolume --> tmpVolume { float scale = (float)tmpVolume.Width / srcVolume.Width; float inverseScale = 1.0f / scale; #if SINGLE_THREADED for (int z = 0; z < tmpVolume.Depth; z++) #else Parallel.For(0, tmpVolume.Depth, z => #endif { for (int y = 0; y < tmpVolume.Height; y++) { // Apply polyphase kernel horizontally. for (int x = 0; x < tmpVolume.Width; x++) { float center = (x + 0.5f) * inverseScale; int left = (int)Math.Floor(center - kernelX.Width); int right = (int)Math.Ceiling(center + kernelX.Width); Debug.Assert(right - left <= kernelX.WindowSize); float totalRgbWeights = 0.0f; Vector4F sum = new Vector4F(); for (int i = 0; i < kernelX.WindowSize; i++) { Vector4F color = srcVolume.GetPixel(left + i, y, z, wrapMode); const float alphaEpsilon = 1.0f / 256.0f; float alpha = alphaTransparency ? color.W + alphaEpsilon : 1.0f; float weight = kernelX.Weights[x, i]; float rgbWeight = weight * alpha; totalRgbWeights += rgbWeight; sum.X += color.X * rgbWeight; sum.Y += color.Y * rgbWeight; sum.Z += color.Z * rgbWeight; sum.W += color.W * weight; } float f = 1 / totalRgbWeights; sum.X *= f; sum.Y *= f; sum.Z *= f; tmpVolume.SetPixel(x, y, z, sum); } } } #if !SINGLE_THREADED ); #endif } // Resize vertically: tmpVolume --> tmpVolume2 { float scale = (float)tmpVolume2.Height / tmpVolume.Height; float inverseScale = 1.0f / scale; #if SINGLE_THREADED for (int z = 0; z < tmpVolume2.Depth; z++) #else Parallel.For(0, tmpVolume2.Depth, z => #endif { for (int x = 0; x < tmpVolume2.Width; x++) { // Apply polyphase kernel vertically. for (int y = 0; y < tmpVolume2.Height; y++) { float center = (y + 0.5f) * inverseScale; int left = (int)Math.Floor(center - kernelY.Width); int right = (int)Math.Ceiling(center + kernelY.Width); Debug.Assert(right - left <= kernelY.WindowSize); float totalRgbWeights = 0.0f; Vector4F sum = new Vector4F(); for (int i = 0; i < kernelY.WindowSize; i++) { Vector4F color = tmpVolume.GetPixel(x, left + i, z, wrapMode); const float alphaEpsilon = 1.0f / 256.0f; float alpha = alphaTransparency ? color.W + alphaEpsilon : 1.0f; float weight = kernelY.Weights[y, i]; float rgbWeight = weight * alpha; totalRgbWeights += rgbWeight; sum.X += color.X * rgbWeight; sum.Y += color.Y * rgbWeight; sum.Z += color.Z * rgbWeight; sum.W += color.W * weight; } float f = 1 / totalRgbWeights; sum.X *= f; sum.Y *= f; sum.Z *= f; tmpVolume2.SetPixel(x, y, z, sum); } } } #if !SINGLE_THREADED ); #endif } // Resize depth: tmpVolume2 --> dstVolume { float scale = (float)dstVolume.Depth / tmpVolume2.Depth; float inverseScale = 1.0f / scale; #if SINGLE_THREADED for (int y = 0; y < dstVolume.Height; y++) #else Parallel.For(0, dstVolume.Height, y => #endif { for (int x = 0; x < dstVolume.Width; x++) { // Apply polyphase kernel along z direction. for (int z = 0; z < dstVolume.Depth; z++) { float center = (z + 0.5f) * inverseScale; int left = (int)Math.Floor(center - kernelZ.Width); int right = (int)Math.Ceiling(center + kernelZ.Width); Debug.Assert(right - left <= kernelZ.WindowSize); float totalRgbWeights = 0.0f; Vector4F sum = new Vector4F(); for (int i = 0; i < kernelZ.WindowSize; i++) { Vector4F color = tmpVolume2.GetPixel(x, y, left + i, wrapMode); const float alphaEpsilon = 1.0f / 256.0f; float alpha = alphaTransparency ? color.W + alphaEpsilon : 1.0f; float weight = kernelZ.Weights[z, i]; float rgbWeight = weight * alpha; totalRgbWeights += rgbWeight; sum.X += color.X * rgbWeight; sum.Y += color.Y * rgbWeight; sum.Z += color.Z * rgbWeight; sum.W += color.W * weight; } float f = 1 / totalRgbWeights; sum.X *= f; sum.Y *= f; sum.Z *= f; dstVolume.SetPixel(x, y, z, sum); } } } #if !SINGLE_THREADED ); #endif } } // ReSharper restore AccessToDisposedClosure }
/// <summary> /// Compresses the specified texture using a Block Compression format (BC<i>n</i>). /// </summary> /// <param name="texture">The uncompressed texture.</param> /// <param name="inputGamma">The input gamma.</param> /// <param name="outputGamma">The output gamma.</param> /// <param name="generateMipmaps"> /// <see langword="true"/> to generate all mipmap levels; otherwise <see langword="false"/>. /// </param> /// <param name="hasAlpha"> /// <see langword="true"/> if <paramref name="texture"/> requires an alpha channel; otherwise, /// <see langword="false"/> if <paramref name="texture"/> is opaque. /// </param> /// <param name="hasFractionalAlpha"> /// <see langword="true"/> if <paramref name="texture"/> contains fractional alpha values; /// otherwise, <see langword="false"/> if <paramref name="texture"/> is opaque or contains only /// binary alpha. /// </param> /// <param name="premultipliedAlpha"> /// <see langword="true"/> when <paramref name="texture"/> is using premultiplied alpha.; /// otherwise, <see langword="false"/>.</param> /// <param name="sharpAlpha"> /// <see langword="true"/> when the texture contains a sharp alpha mask; otherwise /// <see langword="false"/>. /// </param> /// <returns>The compressed texture.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="texture"/> is <see langword="null"/>. /// </exception> /// <exception cref="ArgumentException"> /// Texture width and height need to be equal (square texture) and a power of two (POT /// texture). /// </exception> internal static Texture CompressBCn(Texture texture, float inputGamma, float outputGamma, bool generateMipmaps, bool hasAlpha, bool hasFractionalAlpha, bool premultipliedAlpha, bool sharpAlpha = false) { if (texture == null) throw new ArgumentNullException("texture"); if (texture.Description.Dimension == TextureDimension.Texture3D) throw new NotSupportedException("Texture compression for volume textures is not supported."); // NVIDIA Texture Tools expect BGRA 8:8:8:8. texture = texture.ConvertTo(TextureFormat.B8G8R8A8_UNorm); // ----- InputOptions var inputOptions = new InputOptions(); inputOptions.SetAlphaMode(hasAlpha ? (premultipliedAlpha ? AlphaMode.Premultiplied : AlphaMode.Transparency) : AlphaMode.None); inputOptions.SetFormat(InputFormat.BGRA_8UB); inputOptions.SetGamma(inputGamma, outputGamma); inputOptions.SetMipmapFilter(MipmapFilter.Box); inputOptions.SetMipmapGeneration(generateMipmaps); inputOptions.SetRoundMode(RoundMode.None); // Size is set explicitly. inputOptions.SetWrapMode(WrapMode.Mirror); var description = texture.Description; bool isCube = description.Dimension == TextureDimension.TextureCube; var textureType = isCube ? TextureType.TextureCube : TextureType.Texture2D; inputOptions.SetTextureLayout(textureType, description.Width, description.Height, 1); for (int arrayIndex = 0; arrayIndex < description.ArraySize; arrayIndex++) { for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { int index = texture.GetImageIndex(mipIndex, arrayIndex, 0); var image = texture.Images[index]; var handle = GCHandle.Alloc(image.Data, GCHandleType.Pinned); inputOptions.SetMipmapData(handle.AddrOfPinnedObject(), image.Width, image.Height, 1, arrayIndex, mipIndex); handle.Free(); } } // ----- OutputOptions var outputOptions = new OutputOptions(); outputOptions.SetOutputHeader(false); outputOptions.Error += OnError; Format compressedFormat; if (hasAlpha) { if (sharpAlpha) { compressedFormat = Format.BC2; description.Format = TextureFormat.BC2_UNorm; } else { compressedFormat = Format.BC3; description.Format = TextureFormat.BC3_UNorm; } } else { compressedFormat = Format.BC1; description.Format = TextureFormat.BC1_UNorm; } var compressedTexture = new Texture(description); var outputHandler = new OutputHandler(compressedTexture); outputOptions.SetOutputHandler(outputHandler.BeginImage, outputHandler.WriteData); // ----- CompressionOptions var compressionOptions = new CompressionOptions(); compressionOptions.SetFormat(compressedFormat); compressionOptions.SetQuality(Quality.Normal); // ----- Run NVTT try { var compressor = new Compressor(); compressor.Compress(inputOptions, compressionOptions, outputOptions); } catch (NullReferenceException) { throw new Exception("NullReferenceException in NVIDIA texture tools. Please try again."); } return compressedTexture; }
//-------------------------------------------------------------- /// <summary> /// Converts an XNA <see cref="TextureContent"/> to a DigitalRune <see cref="Texture"/>. /// </summary> /// <param name="textureContent">The <see cref="TextureContent"/>.</param> /// <returns>The <see cref="Texture"/>.</returns> public static Texture ToTexture(TextureContent textureContent) { SurfaceFormat surfaceFormat; var bitmapContent0 = textureContent.Faces[0][0]; if (!bitmapContent0.TryGetFormat(out surfaceFormat)) throw new InvalidContentException("Invalid surface format.", textureContent.Identity); var texture2DContent = textureContent as Texture2DContent; if (texture2DContent != null) { var description = new TextureDescription { Dimension = TextureDimension.Texture2D, Width = bitmapContent0.Width, Height = bitmapContent0.Height, Depth = 1, MipLevels = texture2DContent.Mipmaps.Count, ArraySize = 1, Format = surfaceFormat.ToDataFormat() }; var texture = new Texture(description); for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { var bitmapContent = texture2DContent.Mipmaps[mipIndex]; var image = texture.Images[texture.GetImageIndex(mipIndex, 0, 0)]; Buffer.BlockCopy(bitmapContent.GetPixelData(), 0, image.Data, 0, image.Data.Length); } return texture; } var textureCubeContent = textureContent as TextureCubeContent; if (textureCubeContent != null) { var description = new TextureDescription { Dimension = TextureDimension.TextureCube, Width = bitmapContent0.Width, Height = bitmapContent0.Height, Depth = 1, MipLevels = textureCubeContent.Faces[0].Count, ArraySize = 6, Format = surfaceFormat.ToDataFormat() }; var texture = new Texture(description); for (int faceIndex = 0; faceIndex < 6; faceIndex++) { for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { var bitmapContent = textureCubeContent.Faces[faceIndex][mipIndex]; var image = texture.Images[texture.GetImageIndex(mipIndex, faceIndex, 0)]; Buffer.BlockCopy(bitmapContent.GetPixelData(), 0, image.Data, 0, image.Data.Length); } } return texture; } var texture3DContent = textureContent as Texture3DContent; if (texture3DContent != null) { var description = new TextureDescription { Dimension = TextureDimension.Texture3D, Width = bitmapContent0.Width, Height = bitmapContent0.Height, Depth = texture3DContent.Faces.Count, MipLevels = texture3DContent.Faces[0].Count, ArraySize = 1, Format = surfaceFormat.ToDataFormat() }; var texture = new Texture(description); for (int zIndex = 0; zIndex < description.Depth; zIndex++) { for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { var bitmapContent = texture3DContent.Faces[zIndex][mipIndex]; var image = texture.Images[texture.GetImageIndex(mipIndex, 0, zIndex)]; Buffer.BlockCopy(bitmapContent.GetPixelData(), 0, image.Data, 0, image.Data.Length); } } return texture; } throw new InvalidOperationException("Invalid texture dimension."); }