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 }