public Texture LoadTextureFromImage <T>(Image <T> image, string name     = null,
                                                TextureLoadParameters?loadParams = null) where T : unmanaged, IPixel <T>
        {
            DebugTools.Assert(_mainThread == Thread.CurrentThread);

            var actualParams = loadParams ?? TextureLoadParameters.Default;
            var pixelType    = typeof(T);

            // Flip image because OpenGL reads images upside down.
            var copy = FlipClone(image);

            var texture = new GLHandle((uint)GL.GenTexture());

            GL.BindTexture(TextureTarget.Texture2D, texture.Handle);
            ApplySampleParameters(actualParams.SampleParameters);

            PixelInternalFormat internalFormat;
            PixelFormat         pixelDataFormat;
            PixelType           pixelDataType;

            if (pixelType == typeof(Rgba32))
            {
                internalFormat  = actualParams.Srgb ? PixelInternalFormat.Srgb8Alpha8 : PixelInternalFormat.Rgba8;
                pixelDataFormat = PixelFormat.Rgba;
                pixelDataType   = PixelType.UnsignedByte;
            }
            else if (pixelType == typeof(Alpha8))
            {
                if (image.Width % 4 != 0 || image.Height % 4 != 0)
                {
                    throw new ArgumentException("Alpha8 images must have multiple of 4 sizes.");
                }
                internalFormat  = PixelInternalFormat.R8;
                pixelDataFormat = PixelFormat.Red;
                pixelDataType   = PixelType.UnsignedByte;

                // TODO: Does it make sense to default to 1 for RGB parameters?
                // It might make more sense to pass some options to change swizzling.
                var swizzle = new[] { (int)All.One, (int)All.One, (int)All.One, (int)All.Red };
                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleRgba, swizzle);
            }
            else if (pixelType == typeof(Gray8) && !actualParams.Srgb)
            {
                // Can only use R8 for Gray8 if sRGB is OFF.
                // Because OpenGL doesn't provide non-sRGB single/dual channel image formats.
                // Vulkan when?
                if (copy.Width % 4 != 0 || copy.Height % 4 != 0)
                {
                    throw new ArgumentException("Gray8 non-sRGB images must have multiple of 4 sizes.");
                }

                internalFormat  = PixelInternalFormat.R8;
                pixelDataFormat = PixelFormat.Red;
                pixelDataType   = PixelType.UnsignedByte;

                var swizzle = new[] { (int)All.Red, (int)All.Red, (int)All.Red, (int)All.One };
                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleRgba, swizzle);
            }
            else
            {
                throw new NotImplementedException($"Unable to handle pixel type '{pixelType.Name}'");
            }

            unsafe
            {
                var span = copy.GetPixelSpan();
                fixed(T *ptr = span)
                {
                    GL.TexImage2D(TextureTarget.Texture2D, 0, internalFormat, copy.Width, copy.Height, 0,
                                  pixelDataFormat, pixelDataType, (IntPtr)ptr);
                }
            }

            return(GenTexture(texture, (copy.Width, copy.Height), name));
        }
Beispiel #2
0
        public Texture LoadTextureFromImage <T>(Image <T> image, string?name     = null,
                                                TextureLoadParameters?loadParams = null) where T : unmanaged, IPixel <T>
        {
            DebugTools.Assert(_mainThread == Thread.CurrentThread);

            var actualParams = loadParams ?? TextureLoadParameters.Default;
            var pixelType    = typeof(T);

            if (!_hasGLTextureSwizzle)
            {
                // If texture swizzle isn't available we have to pre-process the images to apply it ourselves
                // and then upload as RGBA8.
                // Yes this is inefficient but the alternative is modifying the shaders,
                // which I CBA to do.
                // Even 8 year old iGPUs support texture swizzle.
                if (pixelType == typeof(A8))
                {
                    // Disable sRGB so stuff doesn't get interpreter wrong.
                    actualParams.Srgb = false;
                    var img = ApplyA8Swizzle((Image <A8>)(object) image);
                    return(LoadTextureFromImage(img, name, loadParams));
                }

                if (pixelType == typeof(L8) && !actualParams.Srgb)
                {
                    var img = ApplyL8Swizzle((Image <L8>)(object) image);
                    return(LoadTextureFromImage(img, name, loadParams));
                }
            }

            // Flip image because OpenGL reads images upside down.
            var copy = FlipClone(image);

            var texture = new GLHandle((uint)GL.GenTexture());

            CheckGlError();
            GL.BindTexture(TextureTarget.Texture2D, texture.Handle);
            CheckGlError();
            ApplySampleParameters(actualParams.SampleParameters);

            PixelInternalFormat internalFormat;
            PixelFormat         pixelDataFormat;
            PixelType           pixelDataType;

            if (pixelType == typeof(Rgba32))
            {
                internalFormat  = actualParams.Srgb ? PixelInternalFormat.Srgb8Alpha8 : PixelInternalFormat.Rgba8;
                pixelDataFormat = PixelFormat.Rgba;
                pixelDataType   = PixelType.UnsignedByte;
            }
            else if (pixelType == typeof(A8))
            {
                if (image.Width % 4 != 0 || image.Height % 4 != 0)
                {
                    throw new ArgumentException("Alpha8 images must have multiple of 4 sizes.");
                }

                internalFormat  = PixelInternalFormat.R8;
                pixelDataFormat = PixelFormat.Red;
                pixelDataType   = PixelType.UnsignedByte;

                unsafe
                {
                    // TODO: Does it make sense to default to 1 for RGB parameters?
                    // It might make more sense to pass some options to change swizzling.
                    var swizzle = stackalloc[] { (int)All.One, (int)All.One, (int)All.One, (int)All.Red };
                    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleRgba, swizzle);
                }
            }
            else if (pixelType == typeof(L8) && !actualParams.Srgb)
            {
                // Can only use R8 for L8 if sRGB is OFF.
                // Because OpenGL doesn't provide sRGB single/dual channel image formats.
                // Vulkan when?
                if (copy.Width % 4 != 0 || copy.Height % 4 != 0)
                {
                    throw new ArgumentException("L8 non-sRGB images must have multiple of 4 sizes.");
                }

                internalFormat  = PixelInternalFormat.R8;
                pixelDataFormat = PixelFormat.Red;
                pixelDataType   = PixelType.UnsignedByte;

                unsafe
                {
                    var swizzle = stackalloc[] { (int)All.Red, (int)All.Red, (int)All.Red, (int)All.One };
                    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleRgba, swizzle);
                }
            }
            else
            {
                throw new NotImplementedException($"Unable to handle pixel type '{pixelType.Name}'");
            }

            unsafe
            {
                var span = copy.GetPixelSpan();
                fixed(T *ptr = span)
                {
                    GL.TexImage2D(TextureTarget.Texture2D, 0, internalFormat, copy.Width, copy.Height, 0,
                                  pixelDataFormat, pixelDataType, (IntPtr)ptr);
                    CheckGlError();
                }
            }

            var pressureEst = EstPixelSize(internalFormat) * copy.Width * copy.Height;

            return(GenTexture(texture, (copy.Width, copy.Height), name, pressureEst));
        }