Beispiel #1
0
        private void Swap(ref TextureArray2D t1, ref TextureArray2D t2)
        {
            var tmp = t1;

            t1 = t2;
            t2 = tmp;
        }
Beispiel #2
0
        public void LoadKtxCubemap()
        {
            var tex = new TextureArray2D(IO.LoadImage(TestData.Directory + "cubemap.ktx"));

            Assert.AreEqual(6, tex.NumLayers);
            Assert.AreEqual(3, tex.NumMipmaps);
        }
Beispiel #3
0
        private void SaveMultipleLevel(ExportModel info, TextureArray2D texture)
        {
            Debug.Assert(info.TexFormat.Format.HasGliFormat);

            var numLayer        = info.Layer == -1 ? models.Images.NumLayers : 1;
            var numLevels       = info.Mipmap == -1 ? models.Images.NumMipmaps : 1;
            var supportCropping = numLevels == 1;

            var width  = info.GetCropWidth();
            var height = info.GetCropHeight();

            if (!info.UseCropping && numLevels == 1)
            {
                // set full width and height
                width  = models.Images.GetWidth(info.Mipmap);
                height = models.Images.GetHeight(info.Mipmap);
            }

            Debug.Assert(width > 0);
            Debug.Assert(height > 0);
            if (!supportCropping)
            {
                width  = models.Images.Width;
                height = models.Images.Height;
            }

            // allocate
            ImageLoader.CreateStorage(info.TexFormat.Format.GliFormat, width, height, numLayer, numLevels);

            // store data
            for (var layerIdx = 0; layerIdx < numLayer; ++layerIdx)
            {
                for (var levelIdx = 0; levelIdx < numLevels; ++levelIdx)
                {
                    ImageLoader.GetLevelSize(levelIdx, out var bufSize);
                    var data = texture.GetData(
                        numLayer == 1 ? info.Layer : layerIdx,
                        numLevels == 1 ? info.Mipmap : levelIdx,
                        info.TexFormat.Format, info.UseCropping && supportCropping,
                        info.CropStartX, info.CropStartY, ref width, ref height,
                        models.GlData.ExportShader, (int)bufSize);

                    ImageLoader.StoreLevel(layerIdx, levelIdx, data, (UInt64)data.Length);
                }
            }

            // save texture
            if (info.FileType == ExportModel.FileFormat.Ktx)
            {
                ImageLoader.SaveKtx(info.Filename);
            }
            else if (info.FileType == ExportModel.FileFormat.Ktx2)
            {
                ImageLoader.SaveKtx2(info.Filename);
            }
            else if (info.FileType == ExportModel.FileFormat.Dds)
            {
                ImageLoader.SaveDDS(info.Filename);
            }
        }
Beispiel #4
0
        public static void CompareWithSmall(Image image, Color.Channel channels)
        {
            var tex    = new TextureArray2D(image);
            var colors = tex.GetPixelColors(0, 0);

            CompareWithSmall(colors, channels);
        }
        public void TestMipmaps()
        {
            // load checkers texture
            var tex = new TextureArray2D(IO.LoadImage(TestData.Directory + "checkers.dds"));

            Assert.AreEqual(3, tex.NumMipmaps);
            Assert.AreEqual(4, tex.Size.Width);
            Assert.AreEqual(4, tex.Size.Height);
            TestData.TestCheckersLevel0(tex.GetPixelColors(0, 0));
            TestData.TestCheckersLevel1(tex.GetPixelColors(0, 1));
            TestData.TestCheckersLevel2(tex.GetPixelColors(0, 2));

            // remove mipmaps
            tex = tex.CloneWithoutMipmapsT();
            Assert.AreEqual(1, tex.NumMipmaps);
            Assert.AreEqual(4, tex.Size.Width);
            Assert.AreEqual(4, tex.Size.Height);
            TestData.TestCheckersLevel0(tex.GetPixelColors(0, 0));

            // generate mipmaps again
            tex = tex.GenerateMipmapLevelsT(3);
            Assert.AreEqual(3, tex.NumMipmaps);
            Assert.AreEqual(4, tex.Size.Width);
            Assert.AreEqual(4, tex.Size.Height);
            TestData.TestCheckersLevel0(tex.GetPixelColors(0, 0));
            TestData.TestCheckersLevel1(tex.GetPixelColors(0, 1));
            TestData.TestCheckersLevel2(tex.GetPixelColors(0, 2));
        }
        public void SSIMMultiscaleLuminance()
        {
            var s       = new MultiscaleSSIMShader();
            var toRed   = new ImageFramework.Model.Shader.TransformShader("return value.r;", "float4", "float");
            var fromRed = new ImageFramework.Model.Shader.TransformShader("return float4(value, value, value, 1.0);", "float", "float4");
            var tex     = IO.LoadImageTexture(TestData.Directory + "checkers.dds");
            var redTex  = new TextureArray2D(tex.LayerMipmap, tex.Size, Format.R32_Float, true);
            var dstTex  = new TextureArray2D(tex.LayerMipmap, tex.Size, Format.R32G32B32A32_Float, true);
            var upload  = new UploadBuffer(256);

            Assert.AreEqual(tex.NumMipmaps, 3);
            var expected = tex.GetPixelColors(LayerMipmapSlice.Mip2)[0];

            // copy checkers red channel only
            foreach (var lm in tex.LayerMipmap.Range)
            {
                toRed.Run(tex, redTex, lm, upload);
            }

            // this should copy lowest resolution mipmap to first mipmap
            s.RunCopy(redTex, LayerMipmapSlice.Mip0, upload);

            // copy back
            foreach (var lm in tex.LayerMipmap.Range)
            {
                fromRed.Run(redTex, dstTex, lm, upload);
            }

            var actual = dstTex.GetPixelColors(LayerMipmapSlice.Mip0);

            foreach (var color in actual)
            {
                Assert.IsTrue(color.Equals(expected, Color.Channel.R, 0.0011f));
            }
        }
Beispiel #7
0
        // converts a lat long texture to a cubemap
        public TextureArray2D ConvertToCube(TextureArray2D latlong, int resolution)
        {
            Debug.Assert(latlong.NumLayers == 1);

            var dst = new TextureArray2D(new LayerMipmapCount(6, 1), new Size3(resolution, resolution), Format.R32G32B32A32_Float, false);

            var dev = Device.Get();

            quad.Bind(false);
            dev.Pixel.Set(toCube.Pixel);

            dev.Pixel.SetShaderResource(0, latlong.GetSrView(LayerMipmapSlice.Mip0));
            dev.Pixel.SetSampler(0, sampler);
            dev.OutputMerger.SetRenderTargets(null,
                                              dst.GetRtView(new LayerMipmapSlice(0, 0)),
                                              dst.GetRtView(new LayerMipmapSlice(1, 0)),
                                              dst.GetRtView(new LayerMipmapSlice(2, 0)),
                                              dst.GetRtView(new LayerMipmapSlice(3, 0)),
                                              dst.GetRtView(new LayerMipmapSlice(4, 0)),
                                              dst.GetRtView(new LayerMipmapSlice(5, 0)));
            dev.SetViewScissors(resolution, resolution);
            dev.DrawQuad();

            dev.Pixel.SetShaderResource(0, null);
            dev.Pixel.SetSampler(0, null);
            dev.OutputMerger.SetRenderTargets((RenderTargetView)null);
            quad.Unbind();

            return(dst);
        }
        private void Apply(TextureArray2D src, TextureArray2D dst, int dirX, int dirY)
        {
            quad.Bind(false);
            var dev = Device.Get();

            dev.Pixel.Set(shader.Pixel);

            cbuffer.SetData(new DirSizeData
            {
                DirX  = dirX,
                DirY  = dirY,
                SizeX = src.Size.Width,
                SizeY = src.Size.Height
            });

            dev.Pixel.SetConstantBuffer(0, cbuffer.Handle);
            dev.SetViewScissors(dst.Size.Width, dst.Size.Height);

            foreach (var lm in src.LayerMipmap.LayersOfMipmap(0))
            {
                dev.Pixel.SetShaderResource(0, src.GetSrView(lm));
                dev.OutputMerger.SetRenderTargets(dst.GetRtView(lm));
                dev.DrawQuad();
            }

            dev.Pixel.SetShaderResource(0, null);
            dev.OutputMerger.SetRenderTargets((RenderTargetView)null);
            quad.Unbind();
        }
Beispiel #9
0
        public static void CompareWithSmall(DllImageData image, Color.Channel channels)
        {
            var tex    = new TextureArray2D(image);
            var colors = tex.GetPixelColors(LayerMipmapSlice.Mip0);

            CompareWithSmall(colors, channels);
        }
Beispiel #10
0
 /// <summary>
 /// returns colors from the given filename within the test directory
 /// </summary>
 public static Color[] GetColors(string filename)
 {
     using (var tex = new TextureArray2D(IO.LoadImage(Directory + filename)))
     {
         return(tex.GetPixelColors(LayerMipmapSlice.Mip0));
     }
 }
Beispiel #11
0
        /// <summary>
        /// copies a single mip from one layer of a texture to another layer of a texture. The formats don't have to match
        /// </summary>
        /// <param name="src">source texture</param>
        /// <param name="srcLayer"></param>
        /// <param name="srcMip"></param>
        /// <param name="dst">destination texture</param>
        /// <param name="dstLayer"></param>
        /// <param name="dstMip"></param>
        public void CopyLayer(TextureArray2D src, int srcLayer, int srcMip, TextureArray2D dst, int dstLayer,
                              int dstMip)
        {
            Debug.Assert(src.Size == dst.Size);

            var dev = DirectX.Device.Get();

            quad.Bind(false);
            dev.Pixel.Set(convert2D.Pixel);

            dev.Pixel.SetShaderResource(0, src.View);


            cbuffer.SetData(new LayerLevelOffsetData
            {
                Layer      = srcLayer,
                Level      = srcMip,
                Xoffset    = 0,
                Yoffset    = 0,
                Multiplier = 1.0f
            });

            var dim = dst.Size.GetMip(dstMip);

            dev.Pixel.SetConstantBuffer(0, cbuffer.Handle);
            dev.OutputMerger.SetRenderTargets(dst.GetRtView(dstLayer, dstMip));
            dev.SetViewScissors(dim.Width, dim.Height);
            dev.DrawQuad();

            // remove bindings
            dev.Pixel.SetShaderResource(0, null);
            dev.OutputMerger.SetRenderTargets((RenderTargetView)null);
        }
Beispiel #12
0
        public override void Draw(TextureArray2D texture)
        {
            // this will draw the checkers background
            base.Draw(texture);

            GL.Enable(EnableCap.Blend);
            GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);

            // bind shader
            shader.Bind();
            shader.SetTransform(GetTransform());
            shader.SetMipmap((float)models.Display.ActiveMipmap);
            shader.SetLayer((float)models.Display.ActiveLayer);
            shader.SetFarplane(CalcFarplane());
            shader.SetGrayscale(models.Display.Grayscale);
            shader.SetCrop(models.Export, models.Display.ActiveLayer);

            models.GlData.BindSampler(shader.GetTextureLocation(), true, models.Display.LinearInterpolation);
            texture.Bind(shader.GetTextureLocation());

            models.GlData.Vao.DrawQuad();

            GL.Disable(EnableCap.Blend);
            Program.Unbind();
        }
Beispiel #13
0
        public void FastGaussTest()
        {
            // filter kernel:
            // 1 2 1
            // 2 4 2
            // 1 2 1

            var s   = new FastGaussShader();
            var img = IO.LoadImageTexture(TestData.Directory + "small.pfm");
            var dst = new TextureArray2D(LayerMipmapCount.One, img.Size, Format.R32G32B32A32_Float, true);

            s.Run(img, dst, 0, false, new UploadBuffer(256));

            var src = img.GetPixelColors(LayerMipmapSlice.Mip0);
            var res = dst.GetPixelColors(LayerMipmapSlice.Mip0);

            Assert.AreEqual(3 * 3, res.Length);

            // expected values calculated by hand
            float midR = (src[0].Red + src[1].Red * 2.0f + src[2].Red
                          + src[3].Red * 2.0f + src[4].Red * 4.0f + src[5].Red * 2.0f
                          + src[6].Red + src[7].Red * 2.0f + src[8].Red) / 16.0f;
            float midG = (src[0].Green + src[1].Green * 2.0f + src[2].Green
                          + src[3].Green * 2.0f + src[4].Green * 4.0f + src[5].Green * 2.0f
                          + src[6].Green + src[7].Green * 2.0f + src[8].Green) / 16.0f;
            var midColor = new Color(midR, midG, 1.0f);

            float firstR = (src[0].Red * 4.0f + src[1].Red * 2.0f
                            + src[3].Red * 2.0f + src[4].Red) / 9.0f;
            var firstColor = new Color(firstR);

            Assert.IsTrue(res[4].Equals(midColor, Color.Channel.R | Color.Channel.G));
            Assert.IsTrue(res[0].Equals(firstColor, Color.Channel.R));
        }
Beispiel #14
0
        /// <summary>
        /// for unit testing purposes. Converts naked srv to TextureArray2D
        /// </summary>
        internal TextureArray2D ConvertFromRaw(SharpDX.Direct3D11.ShaderResourceView srv, Size3 size, SharpDX.DXGI.Format dstFormat)
        {
            var res = new TextureArray2D(1, 1, size, dstFormat, false);

            var dev = DirectX.Device.Get();

            quad.Bind(false);
            dev.Pixel.Set(convert2D.Pixel);

            dev.Pixel.SetShaderResource(0, srv);

            cbuffer.SetData(new LayerLevelOffsetData
            {
                Layer      = 0,
                Level      = 0,
                Xoffset    = 0,
                Yoffset    = 0,
                Multiplier = 1.0f
            });

            dev.Pixel.SetConstantBuffer(0, cbuffer.Handle);
            dev.OutputMerger.SetRenderTargets(res.GetRtView(0, 0));
            dev.SetViewScissors(size.Width, size.Height);
            dev.DrawQuad();

            // remove bindings
            dev.Pixel.SetShaderResource(0, null);
            dev.OutputMerger.SetRenderTargets((RenderTargetView)null);
            quad.Unbind();

            return(res);
        }
Beispiel #15
0
        /// converts a cubemap texture to a lat long map
        public TextureArray2D ConvertToLatLong(TextureArray2D cube, int resolution)
        {
            Debug.Assert(cube.NumLayers == 6);

            var dst = new TextureArray2D(LayerMipmapCount.One, new Size3(resolution, Math.Max(resolution / 2, 1)), Format.R32G32B32A32_Float, false);

            var dev = Device.Get();

            quad.Bind(false);
            dev.Pixel.Set(toLatLong.Pixel);

            var dim = dst.Size;

            dev.Pixel.SetShaderResource(0, cube.GetCubeView(0));
            dev.Pixel.SetSampler(0, sampler);
            dev.OutputMerger.SetRenderTargets(dst.GetRtView(LayerMipmapSlice.Mip0));
            dev.SetViewScissors(dim.Width, dim.Height);
            dev.DrawQuad();

            dev.Pixel.SetShaderResource(0, null);
            dev.Pixel.SetSampler(0, null);
            dev.OutputMerger.SetRenderTargets((RenderTargetView)null);
            quad.Unbind();

            return(dst);
        }
Beispiel #16
0
        protected void DrawLayer(Matrix4 offset, int layer, TextureArray2D texture)
        {
            Debug.Assert(texture != null);

            var finalTransform = offset * GetTransform();

            // draw the checkers background
            models.GlData.CheckersShader.Bind(finalTransform);
            models.GlData.Vao.DrawQuad();

            // blend over the final image
            GL.Enable(EnableCap.Blend);
            GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);

            shader.Bind();
            shader.SetTransform(finalTransform);
            shader.SetLayer(layer);
            shader.SetMipmap(models.Display.ActiveMipmap);
            shader.SetGrayscale(models.Display.Grayscale);
            shader.SetCrop(models.Export, layer);

            models.GlData.BindSampler(shader.GetTextureLocation(), true, models.Display.LinearInterpolation);
            texture.Bind(shader.GetTextureLocation());

            models.GlData.Vao.DrawQuad();

            // disable everything
            GL.Disable(EnableCap.Blend);
            Program.Unbind();
        }
Beispiel #17
0
        private void CompareAfterExport(string inputImage, string outputImage, string outputExtension, GliFormat format, Color.Channel channels = Color.Channel.Rgb, float tolerance = 0.01f)
        {
            var model = new Models(1);

            model.AddImageFromFile(inputImage);
            model.Apply();
            var origTex = (TextureArray2D)model.Pipelines[0].Image;

            model.Export.Quality = 100;

            model.Export.Export(origTex, new ExportDescription(outputImage, outputExtension, model.Export)
            {
                FileFormat = format
            });
            var expTex = new TextureArray2D(IO.LoadImage(outputImage + "." + outputExtension));

            for (int curLayer = 0; curLayer < origTex.NumLayers; ++curLayer)
            {
                for (int curMipmap = 0; curMipmap < origTex.NumMipmaps; ++curMipmap)
                {
                    var origColors = origTex.GetPixelColors(curLayer, curMipmap);
                    var expColor   = expTex.GetPixelColors(curLayer, curMipmap);

                    TestData.CompareColors(origColors, expColor, channels, tolerance);
                }
            }
        }
        public void ConvertFromSrgb()
        {
            var tex = new TextureArray2D(IO.LoadImage(TestData.Directory + "small_a.png"));

            var newTex = shader.Convert(tex, Format.R32G32B32A32_Float, models.Scaling);

            TestData.CompareWithSmall(newTex.GetPixelColors(LayerMipmapSlice.Mip0), Color.Channel.Rgba);
        }
Beispiel #19
0
 /// <summary>
 /// clears the temporary texture
 /// </summary>
 public void Dispose()
 {
     if (temp != null)
     {
         textureCache.StoreTexture(temp);
         temp = null;
     }
 }
Beispiel #20
0
 public void BindSourceImage(TextureArray2D texture, Sampler sampler, int id)
 {
     sampler.Bind(TextureBindingStart + id);
     texture.Bind(TextureBindingStart + id);
     // set srgb variable
     shader.Bind();
     GL.Uniform1(TextureSrgbStart + id, texture.IsSrgb?1:0);
 }
Beispiel #21
0
 /// <summary>
 /// this texture should contain the final result
 /// </summary>
 /// <returns>valid texture</returns>
 public TextureArray2D GetPrimaryTexture()
 {
     if (primary != null)
     {
         return(primary);
     }
     primary = textureCache.GetTexture();
     return(primary);
 }
Beispiel #22
0
 /// <summary>
 /// dispatches the shader with the given layer and level
 /// source textures have to be bound before a call to this function
 /// </summary>
 /// <param name="layer"></param>
 /// <param name="mipmap"></param>
 /// <param name="width">mipmap width</param>
 /// <param name="height">mipmap height</param>
 /// <param name="target">target image</param>
 public void Run(int layer, int mipmap, int width, int height, TextureArray2D target)
 {
     shader.Bind();
     target.BindAsImage(GetDestinationImageBinding(), layer: layer, mipmap: mipmap, access: TextureAccess.WriteOnly);
     SetLevel(mipmap);
     SetLayer(layer);
     GL.DispatchCompute(width / LocalSize + 1, height / LocalSize + 1, 1);
     Program.Unbind();
 }
        public void ConvertToSrgb()
        {
            // convert from RGBA32F to RGBA8_SRGB
            var tex = new TextureArray2D(IO.LoadImage(TestData.Directory + "small.pfm"));

            var newTex = shader.Convert(tex, Format.R8G8B8A8_UNorm_SRgb, models.Scaling);

            TestData.CompareWithSmall(newTex.GetPixelColors(LayerMipmapSlice.Mip0), Color.Channel.Rgb);
        }
Beispiel #24
0
 /// <summary>
 /// this texture can be used for ping pong rendering
 /// </summary>
 /// <returns>valid texture</returns>
 public TextureArray2D GetTemporaryTexture()
 {
     if (temp != null)
     {
         return(temp);
     }
     temp = textureCache.GetTexture();
     return(temp);
 }
Beispiel #25
0
        public void CreateGif(TextureArray2D left, TextureArray2D right, Config cfg)
        {
            Debug.Assert(left != null);
            Debug.Assert(right != null);
            Debug.Assert(left.Size == right.Size);
            Debug.Assert(!progressModel.IsProcessing);

            var cts = new CancellationTokenSource();

            progressModel.AddTask(CreateGifAsync(left, right, cfg, progressModel.GetProgressInterface(cts.Token)), cts);
        }
Beispiel #26
0
        private async Task CreateGifAsync(TextureArray2D left, TextureArray2D right, Config cfg, IProgress progress)
        {
            // delay in milliseconds
            var numFrames = cfg.FramesPerSecond * cfg.NumSeconds;

            try
            {
                progressModel.EnableDllProgress = false;
                var leftView  = left.GetSrView(LayerMipmapSlice.Mip0);
                var rightView = right.GetSrView(LayerMipmapSlice.Mip0);

                var curProg = progress.CreateSubProgress(0.9f);
                // create frames
                using (var dst = IO.CreateImage(new ImageFormat(Format.R8G8B8A8_UNorm_SRgb), left.Size, LayerMipmapCount.One))
                {
                    var dstMip  = dst.GetMipmap(LayerMipmapSlice.Mip0);
                    var dstPtr  = dstMip.Bytes;
                    var dstSize = dstMip.ByteSize;

                    // render frames into texture
                    using (var frame = new TextureArray2D(LayerMipmapCount.One, left.Size,
                                                          Format.R8G8B8A8_UNorm_SRgb, false))
                    {
                        var frameView = frame.GetRtView(LayerMipmapSlice.Mip0);

                        for (int i = 0; i < numFrames; ++i)
                        {
                            float t         = (float)i / (numFrames);
                            int   borderPos = (int)(t * frame.Size.Width);

                            // render frame
                            shader.Run(leftView, rightView, frameView, cfg.SliderWidth, borderPos,
                                       frame.Size.Width, frame.Size.Height);

                            // save frame as png
                            frame.CopyPixels(LayerMipmapSlice.Mip0, dstPtr, dstSize);
                            var filename = $"{cfg.TmpFilename}{i:D4}";
                            await Task.Run(() => IO.SaveImage(dst, filename, "png", GliFormat.RGBA8_SRGB), progress.Token);

                            curProg.Progress = i / (float)numFrames;
                            curProg.What     = "creating frames";
                        }
                    }
                }

                // convert video
                await FFMpeg.ConvertAsync(cfg, progress.CreateSubProgress(1.0f));
            }
            finally
            {
                progressModel.EnableDllProgress = true;
            }
        }
Beispiel #27
0
        public void MitchelXYScale()
        {
            var shader   = new MitchellNetravaliScaleShader(new QuadShader(), new UploadBuffer(256));
            var checkers = new TextureArray2D(IO.LoadImage(TestData.Directory + "sphere.png"));

            var res = shader.Run(checkers, new Size3(20, 40));

            var reference = new TextureArray2D(IO.LoadImage(TestData.Directory + "sphere_scaled.png"));

            // compare with gimp reference
            TestData.CompareColors(reference.GetPixelColors(0, 0), res.GetPixelColors(0, 0));
        }
        public void Alignment()
        {
            var tex = new TextureArray2D(IO.LoadImage(TestData.Directory + "unaligned.png"));

            Assert.AreEqual(3, tex.Size.Width % 4);
            Assert.AreEqual(1, tex.Size.Height % 4);

            // convert with 4 texel alignment
            var newTex = shader.Convert(tex, Format.R8G8B8A8_UNorm_SRgb, LayerMipmapSlice.Mip0, 1.0f, false, Size3.Zero, Size3.Zero, new Size3(4, 4, 0), models.Scaling);

            Assert.AreEqual(0, newTex.Size.Width % 4);
            Assert.AreEqual(0, newTex.Size.Height % 4);
        }
Beispiel #29
0
        public void MitchelUpscale()
        {
            var models   = new Models(1);
            var shader   = new MitchellNetravaliScaleShader(new QuadShader(), new UploadBuffer(256));
            var checkers = new TextureArray2D(IO.LoadImage(TestData.Directory + "sphere.png"));

            var res = shader.Run(checkers, new Size3(62, 31), models.Scaling);

            var reference = new TextureArray2D(IO.LoadImage(TestData.Directory + "sphere_up.png"));

            // compare with gimp reference
            TestData.CompareColors(reference.GetPixelColors(LayerMipmapSlice.Mip0), res.GetPixelColors(LayerMipmapSlice.Mip0));
        }
Beispiel #30
0
 /// <summary>
 /// stores the textures for later use
 /// </summary>
 /// <param name="tex"></param>
 public void StoreTexture(TextureArray2D tex)
 {
     Debug.Assert(tex != null);
     if (tex.NumMipmaps == images.NumMipmaps)
     {
         // can be used for later
         textures.Push(tex);
     }
     else
     {
         // immediately discard (incompatible image)
         tex.Dispose();
     }
 }