/// <summary> /// scales all images to the given dimensions /// </summary> public void ScaleImages(Size3 size, ScalingModel scaling) { if (NumImages == 0) { return; } if (Size == size) { return; } if (ImageType != typeof(TextureArray2D)) { throw new Exception("scaling is only supported for 2D images"); } var prevMipmaps = NumMipmaps; foreach (var imageData in Images) { imageData.Scale(size, scaleShader, scaling); } InitDimensions(images[0].Image); OnPropertyChanged(nameof(Size)); if (prevMipmaps != NumMipmaps) { OnPropertyChanged(nameof(NumMipmaps)); } }
public void Scale(Size3 size, MitchellNetravaliScaleShader shader, ScalingModel scaling) { var tmp = shader.Run((TextureArray2D)Image, size, scaling); Image.Dispose(); Image = tmp; }
internal void GenerateMipmaps(int levels, ScalingModel scaling) { var tmp = Image.CloneWithMipmaps(levels); scaling.WriteMipmaps(tmp); Image.Dispose(); Image = tmp; }
public TextureArray2D Run(TextureArray2D src, Size3 dstSize, ScalingModel scaling) { Debug.Assert(src.Size != dstSize); var genMipmaps = src.NumMipmaps > 1; var numMipmaps = 1; if (genMipmaps) { numMipmaps = dstSize.MaxMipLevels; } bool changeWidth = dstSize.Width != src.Size.Width; bool changeHeight = dstSize.Height != src.Size.Height; if (changeWidth) { var curMips = numMipmaps; if (changeHeight) // only temporary texture with a single mipmap { curMips = 1; } var tmp = new TextureArray2D(new LayerMipmapCount(src.NumLayers, curMips), new Size3(dstSize.Width, src.Size.Height), src.Format, false); Apply(src, tmp, 1, 0); src = tmp; } if (changeHeight) { var tmp = new TextureArray2D(new LayerMipmapCount(src.NumLayers, numMipmaps), dstSize, src.Format, false); Apply(src, tmp, 0, 1); if (changeWidth) // delete temporary texture created by width invocation { src.Dispose(); } src = tmp; } if (genMipmaps) { scaling.WriteMipmaps(src); } return(src); }
/// <summary> /// generates mipmaps for all images /// </summary> public void GenerateMipmaps(ScalingModel scaling) { Debug.Assert(NumMipmaps == 1); // compute new mipmap levels var levels = Size.MaxMipLevels; if (levels == NumMipmaps) { return; } foreach (var image in Images) { image.GenerateMipmaps(levels, scaling); } // recalc dimensions array InitDimensions(Images[0].Image); OnPropertyChanged(nameof(NumMipmaps)); }
/// <summary> /// creates a thumbnail for one image layer/mipmap /// </summary> /// <param name="size">maximum width/height of the thumbnail</param> /// <param name="texture">source texture</param> /// <param name="dstFormat">destination texture format</param> /// <param name="layer">source layer</param> /// <returns>texture with width, height smaller or equal to size. One layer and one mipmap</returns> public TextureArray2D CreateThumbnail(int size, ITexture texture, SharpDX.DXGI.Format dstFormat, int layer, ScalingModel scaling) { Debug.Assert(ImageFormat.IsSupported(dstFormat)); Debug.Assert(ImageFormat.IsSupported(texture.Format)); // determine dimensions of output texture var width = 0; var height = 0; if (texture.Size.Width > texture.Size.Height) { width = size; height = (texture.Size.Height * size) / texture.Size.Width; } else { height = size; width = (texture.Size.Width * size) / texture.Size.Height; } Debug.Assert(width <= size); Debug.Assert(height <= size); var res = new TextureArray2D(LayerMipmapCount.One, new Size3(width, height), dstFormat, false); // compute which mipmap has the closest fit var mipmap = 0; var curWidth = texture.Size.Width; while (curWidth >= width) { ++mipmap; curWidth /= 2; } // mipmap just jumped over the optimal size mipmap = Math.Max(0, mipmap - 1); var dev = Device.Get(); ITexture tmpTex = null; if (texture.NumMipmaps < mipmap + 1) { // generate new texture with mipmaps tmpTex = texture.CloneWithMipmaps(mipmap + 1); scaling.WriteMipmaps(tmpTex); dev.Pixel.SetShaderResource(0, tmpTex.GetSrView(new LayerMipmapSlice(layer, mipmap))); } else { dev.Pixel.SetShaderResource(0, texture.GetSrView(new LayerMipmapSlice(layer, mipmap))); } quad.Bind(false); if (texture.Is3D) { dev.Pixel.Set(convert3D.Pixel); } else { dev.Pixel.Set(convert2D.Pixel); } dev.Pixel.SetSampler(0, sampler); dev.OutputMerger.SetRenderTargets(res.GetRtView(LayerMipmapSlice.Mip0)); dev.SetViewScissors(width, height); dev.DrawFullscreenTriangle(1); // remove bindings dev.Pixel.SetShaderResource(0, null); dev.OutputMerger.SetRenderTargets((RenderTargetView)null); quad.Unbind(); tmpTex?.Dispose(); return(res); }
/// <summary> /// converts the texture into another format and performs cropping if requested /// </summary> /// <param name="texture">source texture</param> /// <param name="dstFormat">destination format</param> /// <param name="scaling">required for regenerating mipmaps after cropping</param> /// <param name="mipmap">mipmap to export, -1 for all mipmaps</param> /// <param name="layer">layer to export, -1 for all layers</param> /// <param name="multiplier">rgb channels will be multiplied by this value</param> public ITexture Convert(ITexture texture, SharpDX.DXGI.Format dstFormat, ScalingModel scaling, int mipmap = -1, int layer = -1, float multiplier = 1.0f) { return(Convert(texture, dstFormat, new LayerMipmapRange(layer, mipmap), multiplier, false, Size3.Zero, Size3.Zero, Size3.Zero, scaling, null)); }
/// <summary> /// converts the texture into another format and performs cropping if requested /// </summary> /// <param name="texture">source texture</param> /// <param name="dstFormat">destination format</param> /// <param name="srcLm">layer/mipmap to export</param> /// <param name="multiplier">rgb channels will be multiplied by this value</param> /// <param name="crop">indicates if the image should be cropped, only works with 1 mipmap to export</param> /// <param name="offset">if crop: offset in source image</param> /// <param name="size">if crop: size of the destination image</param> /// <param name="align">if nonzero: texture width will be aligned to this (rounded down)</param> /// <param name="scaling">required for regenerating mipmaps after cropping.</param> /// <param name="overlay">overlay</param> /// <param name="scale">scales the destination image by this factor</param> /// <returns></returns> public ITexture Convert(ITexture texture, SharpDX.DXGI.Format dstFormat, LayerMipmapRange srcLm, float multiplier, bool crop, Size3 offset, Size3 size, Size3 align, ScalingModel scaling, ITexture overlay = null, int scale = 1) { Debug.Assert(ImageFormat.IsSupported(dstFormat)); Debug.Assert(ImageFormat.IsSupported(texture.Format)); Debug.Assert(overlay == null || texture.HasSameDimensions(overlay)); Debug.Assert(scale >= 1); // set width, height mipmap int nMipmaps = srcLm.IsSingleMipmap ? 1: texture.NumMipmaps; int nLayer = srcLm.IsSingleLayer ? 1 : texture.NumLayers; // set correct width, height, offsets if (!crop) { size = texture.Size.GetMip(srcLm.FirstMipmap); offset = Size3.Zero; } if (scale != 1) { size.X *= scale; size.Y *= scale; if (texture.Is3D) { size.Z *= scale; } offset.X *= scale; offset.Y *= scale; if (texture.Is3D) { offset.Z *= scale; } } // adjust alignments for (int i = 0; i < 3; ++i) { if (align[i] != 0) { if (size[i] % align[i] != 0) { if (size[i] < align[i]) { throw new Exception($"image needs to be aligned to {align[i]} but one axis is only {size[i]}. Axis should be at least {align[i]}"); } crop = true; var remainder = size[i] % align[i]; offset[i] = offset[i] + remainder / 2; size[i] = size[i] - remainder; } } } bool recomputeMips = nMipmaps > 1 && (crop || scale != 1); if (recomputeMips) { // number of mipmaps might have changed nMipmaps = size.MaxMipLevels; recomputeMips = nMipmaps > 1; } var res = texture.Create(new LayerMipmapCount(nLayer, nMipmaps), size, dstFormat, false); var dev = DirectX.Device.Get(); quad.Bind(texture.Is3D); if (texture.Is3D) { dev.Pixel.Set(convert3D.Pixel); } else { dev.Pixel.Set(convert2D.Pixel); } dev.Pixel.SetShaderResource(0, texture.View); if (overlay != null) { dev.Pixel.SetShaderResource(1, overlay.View); } else { dev.Pixel.SetShaderResource(1, null); } foreach (var dstLm in res.LayerMipmap.Range) { cbuffer.SetData(new LayerLevelOffsetData { Layer = dstLm.Layer + srcLm.FirstLayer + offset.Z, Level = dstLm.Mipmap + srcLm.FirstMipmap, Xoffset = offset.X, Yoffset = offset.Y, Multiplier = multiplier, UseOverlay = overlay != null ? 1 : 0, Scale = scale }); var dim = res.Size.GetMip(dstLm.Mipmap); dev.Pixel.SetConstantBuffer(0, cbuffer.Handle); dev.OutputMerger.SetRenderTargets(res.GetRtView(dstLm)); dev.SetViewScissors(dim.Width, dim.Height); dev.DrawFullscreenTriangle(dim.Depth); if (recomputeMips && dstLm.Mipmap > 0) { break; // only write most detailed mipmap } } // remove bindings dev.Pixel.SetShaderResource(0, null); dev.Pixel.SetShaderResource(1, null); dev.OutputMerger.SetRenderTargets((RenderTargetView)null); quad.Unbind(); if (recomputeMips) { scaling.WriteMipmaps(res); } return(res); }
/// <summary> /// adds pixels to the image edges /// </summary> /// <param name="src">source image</param> /// <param name="leftPad">padding on the left/top/front side</param> /// <param name="rightPad">padding on the right/bot/back side</param> /// <param name="fill">padding fill mode</param> /// <param name="scaling">used for regenerating mipmaps (may be null if no mipmaps need to be generated)</param> /// <param name="shared"></param> /// <param name="keepMipmaps">if set to false, no mipmaps will be generated</param> /// <returns>same as source with added padding (amount of mipmaps might change, format remains)</returns> public ITexture Run(ITexture src, Size3 leftPad, Size3 rightPad, FillMode fill, ScalingModel scaling, SharedModel shared, bool keepMipmaps = true) { Size3 dstSize = leftPad + rightPad + src.Size; int nMipmaps = src.NumMipmaps > 1 ? dstSize.MaxMipLevels : 1; if (!keepMipmaps) { nMipmaps = 1; } var dst = src.Create(new LayerMipmapCount(src.NumLayers, nMipmaps), dstSize, src.Format, src.HasUaViews, true); var dev = DirectX.Device.Get(); shared.Upload.SetData(new BufferData { Depth = dstSize.Depth, Offset = new Float3(leftPad) / new Float3(dstSize), Scale = new Float3(dstSize) / new Float3(src.Size), }); shared.QuadShader.Bind(src.Is3D); if (src.Is3D) { dev.Pixel.Set(shader3D.Pixel); } else { dev.Pixel.Set(shader.Pixel); } dev.Pixel.SetSampler(0, sampler[(int)fill]); dev.Pixel.SetConstantBuffer(0, shared.Upload.Handle); foreach (var lm in src.LayerMipmap.RangeOf(LayerMipmapRange.MostDetailed)) { dev.OutputMerger.SetRenderTargets(dst.GetRtView(lm)); dev.SetViewScissors(dstSize.Width, dstSize.Height); dev.Pixel.SetShaderResource(0, src.GetSrView(lm)); dev.DrawFullscreenTriangle(dstSize.Depth); } // remove bindings shared.QuadShader.Unbind(); dev.Pixel.Set(null); dev.OutputMerger.SetRenderTargets((RenderTargetView)null); dev.Pixel.SetShaderResource(0, null); if (dst.NumMipmaps > 1) { Debug.Assert(scaling != null); scaling.WriteMipmaps(dst); } return(dst); }