/// <summary> /// Create the mipmaps by downsampling using a filter. /// </summary> public void GenerateMipmaps(Filter filter) { for (int i = 1; i < Levels; i++) { TextureData2D src = Data[i - 1]; TextureData2D dst = Data[i]; int srcWidth = src.GetWidth(); int srcHeight = src.GetHeight(); int dstWidth = dst.GetWidth(); int dstHeight = dst.GetHeight(); float[] tmp = new float[dstWidth * srcHeight * Channels]; PolyphaseKernel xKernel = new PolyphaseKernel(filter, srcWidth, dstWidth, 32); PolyphaseKernel yKernel = new PolyphaseKernel(filter, srcHeight, dstHeight, 32); for (int y = 0; y < srcHeight; y++) { xKernel.ApplyHorizontal(y, src, tmp, dstWidth); } for (int x = 0; x < dstWidth; x++) { yKernel.ApplyVertical(x, tmp, dstWidth, srcHeight, Channels, dst); } } }
private static void Resize(Image srcImage, Image dstImage, Filter filter, bool alphaTransparency, TextureAddressMode wrapMode) { if (srcImage == null) throw new ArgumentNullException("srcImage"); if (dstImage == null) throw new ArgumentNullException("dstImage"); if (filter == null) throw new ArgumentNullException("filter"); var kernelX = new PolyphaseKernel(filter, srcImage.Width, dstImage.Width, 32); var kernelY = new PolyphaseKernel(filter, srcImage.Height, dstImage.Height, 32); Resize2D(srcImage, dstImage, alphaTransparency, wrapMode, kernelX, kernelY); }
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); } }
private static void Resize2D(Image srcImage, Image dstImage, bool alphaTransparency, TextureAddressMode wrapMode, PolyphaseKernel kernelX, PolyphaseKernel kernelY) { var tmpImage = new Image(dstImage.Width, srcImage.Height, srcImage.Format); // ReSharper disable AccessToDisposedClosure using (var srcImage4F = new ImageAccessor(srcImage)) using (var tmpImage4F = new ImageAccessor(tmpImage)) using (var dstImage4F = new ImageAccessor(dstImage)) { // Resize horizontally: srcImage --> tmpImage { float scale = (float)tmpImage4F.Width / srcImage4F.Width; float inverseScale = 1.0f / scale; #if SINGLE_THREADED for (int y = 0; y < tmpImage4F.Height; y++) #else Parallel.For(0, tmpImage4F.Height, y => #endif { // Apply polyphase kernel horizontally. for (int x = 0; x < tmpImage4F.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 = srcImage4F.GetPixel(left + i, y, wrapMode); //if (Numeric.IsNaN(color.X) || Numeric.IsNaN(color.Y) || Numeric.IsNaN(color.Z) || Numeric.IsNaN(color.W) // || color.X < 0 || color.Y < 0 || color.Z < 0 || color.W < 0 // || color.X > 1 || color.Y > 1 || color.Z > 1 || color.W > 1) // Debugger.Break(); 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; //if (Numeric.IsNaN(sum.X) || Numeric.IsNaN(sum.Y) || Numeric.IsNaN(sum.Z) || Numeric.IsNaN(sum.W) // || sum.X < 0 || sum.Y < 0 || sum.Z < 0 || sum.W < 0 // || sum.X > 1 || sum.Y > 1 || sum.Z > 1 || sum.W > 1) // Debugger.Break(); } float f = 1 / totalRgbWeights; sum.X *= f; sum.Y *= f; sum.Z *= f; //if (Numeric.IsNaN(sum.X) || Numeric.IsNaN(sum.Y) || Numeric.IsNaN(sum.Z) || Numeric.IsNaN(sum.W) // || sum.X < 0 || sum.Y < 0 || sum.Z < 0 || sum.W < 0 // || sum.X > 1 || sum.Y > 1 || sum.Z > 1 || sum.W > 1) // Debugger.Break(); tmpImage4F.SetPixel(x, y, sum); } } #if !SINGLE_THREADED ); #endif } // Resize vertically: tmpImage --> dstImage { float scale = (float)dstImage4F.Height / tmpImage4F.Height; float inverseScale = 1.0f / scale; #if SINGLE_THREADED for (int x = 0; x < dstImage4F.Width; x++) #else Parallel.For(0, dstImage4F.Width, x => #endif { // Apply polyphase kernel vertically. for (int y = 0; y < dstImage4F.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 = tmpImage4F.GetPixel(x, left + i, 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; dstImage4F.SetPixel(x, y, sum); } } #if !SINGLE_THREADED ); #endif } } // ReSharper restore AccessToDisposedClosure }
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 }