/// <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);
      }
    }
Exemple #4
0
            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;
        }
Exemple #10
0
        /// <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
        }
Exemple #11
0
        /// <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;
              }
        }
Exemple #12
0
        /// <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;
            }
              }
        }
Exemple #13
0
        /// <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);
        }
Exemple #14
0
        /// <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);
        }
Exemple #15
0
        /// <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.");
        }