From ff30e158143ada669cc04870febb1ef826fda3c7 Mon Sep 17 00:00:00 2001 From: Steve Williams Date: Sat, 14 Oct 2017 02:10:17 +1000 Subject: [PATCH] Use StbSharp for all Texture2D.FromStream StbSharp is now used for all implementations of Texture2D.FromStream(GraphicsDevice, Stream), avoiding discrepancies with OS-provided image loading routines. As a result of this, DirectX platforms no longer support TIF or DDS through FromStream(). Added unit tests for loading 1-, 8-, 24- and 32-bit PNGs. Fixes #6001 --- Build/Projects/MonoGame.Framework.definition | 33 ++++--------- Build/Projects/MonoGame.Tests.definition | 13 ++++++ .../Graphics/Texture2D.DirectX.cs | 39 ++++++++++------ .../Graphics/Texture2D.OpenGL.cs | 44 ++++++------------ .../Utilities/Imaging/ImageReader.cs | 4 +- Test/Assets/Textures/1bit.png | Bin 0 -> 81 bytes Test/Assets/Textures/24bit.png | Bin 0 -> 169 bytes Test/Assets/Textures/32bit.png | Bin 0 -> 169 bytes Test/Assets/Textures/8bit.png | Bin 0 -> 938 bytes .../Graphics/Texture2DNonVisualTest.cs | 20 ++++---- 10 files changed, 73 insertions(+), 80 deletions(-) create mode 100644 Test/Assets/Textures/1bit.png create mode 100644 Test/Assets/Textures/24bit.png create mode 100644 Test/Assets/Textures/32bit.png create mode 100644 Test/Assets/Textures/8bit.png diff --git a/Build/Projects/MonoGame.Framework.definition b/Build/Projects/MonoGame.Framework.definition index 246e552e368..a7a26423a3b 100644 --- a/Build/Projects/MonoGame.Framework.definition +++ b/Build/Projects/MonoGame.Framework.definition @@ -1191,30 +1191,15 @@ - - WindowsGL,Linux,MacOS - - - WindowsGL,Linux,MacOS - - - WindowsGL,Linux,MacOS - - - WindowsGL,Linux,MacOS - - - WindowsGL,Linux,MacOS - - - WindowsGL,Linux,MacOS - - - WindowsGL,Linux,MacOS - - - WindowsGL,Linux,MacOS - + + + + + + + + + diff --git a/Build/Projects/MonoGame.Tests.definition b/Build/Projects/MonoGame.Tests.definition index 6465fe8a925..d9ce93bc180 100644 --- a/Build/Projects/MonoGame.Tests.definition +++ b/Build/Projects/MonoGame.Tests.definition @@ -1334,6 +1334,19 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + PreserveNewest diff --git a/MonoGame.Framework/Graphics/Texture2D.DirectX.cs b/MonoGame.Framework/Graphics/Texture2D.DirectX.cs index e90d98a50f7..620647c90de 100644 --- a/MonoGame.Framework/Graphics/Texture2D.DirectX.cs +++ b/MonoGame.Framework/Graphics/Texture2D.DirectX.cs @@ -13,6 +13,7 @@ using SharpDX.WIC; using MapFlags = SharpDX.Direct3D11.MapFlags; using Resource = SharpDX.Direct3D11.Resource; +using MonoGame.Utilities; #if WINDOWS_UAP using Windows.Graphics.Imaging; @@ -214,26 +215,36 @@ private int CalculateSubresourceIndex(int arraySlice, int level) return arraySlice * _levelCount + level; } - private static Texture2D PlatformFromStream(GraphicsDevice graphicsDevice, Stream stream) + private unsafe static Texture2D PlatformFromStream(GraphicsDevice graphicsDevice, Stream stream) { - if (!stream.CanSeek) - throw new NotSupportedException("stream must support seek operations"); + var reader = new ImageReader(); + int width, height, channels; - // For reference this implementation was ultimately found through this post: - // http://stackoverflow.com/questions/9602102/loading-textures-with-sharpdx-in-metro - Texture2D toReturn = null; - SharpDX.WIC.BitmapDecoder decoder; + // The data returned is always four channel BGRA + var data = reader.Read(stream, out width, out height, out channels, Imaging.STBI_rgb_alpha); - using (var bitmap = LoadBitmap(stream, out decoder)) - using (decoder) + // XNA blacks out any pixels with an alpha of zero. + if (channels == 4) { - SharpDX.Direct3D11.Texture2D sharpDxTexture = CreateTex2DFromBitmap(bitmap, graphicsDevice); + fixed (byte* b = &data[0]) + { + for (var i = 0; i < data.Length; i += 4) + { + if (b[i + 3] == 0) + { + b[i + 0] = 0; + b[i + 1] = 0; + b[i + 2] = 0; + } + } + } + } - toReturn = new Texture2D(graphicsDevice, bitmap.Size.Width, bitmap.Size.Height); + Texture2D texture = null; + texture = new Texture2D(graphicsDevice, width, height); + texture.SetData(data); - toReturn._texture = sharpDxTexture; - } - return toReturn; + return texture; } private void PlatformSaveAsJpeg(Stream stream, int width, int height) diff --git a/MonoGame.Framework/Graphics/Texture2D.OpenGL.cs b/MonoGame.Framework/Graphics/Texture2D.OpenGL.cs index a54777a111d..7605923562d 100644 --- a/MonoGame.Framework/Graphics/Texture2D.OpenGL.cs +++ b/MonoGame.Framework/Graphics/Texture2D.OpenGL.cs @@ -258,39 +258,26 @@ private void PlatformConstruct(int width, int height, bool mipmap, SurfaceFormat private static Texture2D PlatformFromStream(GraphicsDevice graphicsDevice, Stream stream) { -#if IOS - using (var uiImage = UIImage.LoadFromData(NSData.FromStream(stream))) - { - var cgImage = uiImage.CGImage; - return PlatformFromStream(graphicsDevice, cgImage); - } -#endif -#if ANDROID - using (Bitmap image = BitmapFactory.DecodeStream(stream, null, new BitmapFactory.Options - { - InScaled = false, - InDither = false, - InJustDecodeBounds = false, - InPurgeable = true, - InInputShareable = true, - })) - { - return PlatformFromStream(graphicsDevice, image); - } -#endif -#if DESKTOPGL || ANGLE var reader = new ImageReader(); - int x, y, comp; - var data = reader.Read(stream, out x, out y, out comp, Imaging.STBI_rgb_alpha); + int width, height, channels; + + // The data returned is always four channel BGRA + var data = reader.Read(stream, out width, out height, out channels, Imaging.STBI_rgb_alpha); // XNA blacks out any pixels with an alpha of zero. - for (var i = 0; i < data.Length; i += 4) + if (channels == 4) { - if (data[i + 3] == 0) + fixed (byte* b = &data[0]) { - data[i + 0] = 0; - data[i + 1] = 0; - data[i + 2] = 0; + for (var i = 0; i < data.Length; i += 4) + { + if (b[i + 3] == 0) + { + b[i + 0] = 0; + b[i + 1] = 0; + b[i + 2] = 0; + } + } } } @@ -299,7 +286,6 @@ private static Texture2D PlatformFromStream(GraphicsDevice graphicsDevice, Strea texture.SetData(data); return texture; -#endif } #if IOS diff --git a/MonoGame.Framework/Utilities/Imaging/ImageReader.cs b/MonoGame.Framework/Utilities/Imaging/ImageReader.cs index 57519217b54..ca4a5b0ba87 100644 --- a/MonoGame.Framework/Utilities/Imaging/ImageReader.cs +++ b/MonoGame.Framework/Utilities/Imaging/ImageReader.cs @@ -64,7 +64,7 @@ public byte[] Read(Stream stream, out int x, out int y, out int comp, int req_co if (result == null) { - throw new Exception(Imaging.LastError); + throw new InvalidOperationException(Imaging.LastError); } // Convert to array @@ -81,4 +81,4 @@ public byte[] Read(Stream stream, out int x, out int y, out int comp, int req_co } } } -} \ No newline at end of file +} diff --git a/Test/Assets/Textures/1bit.png b/Test/Assets/Textures/1bit.png new file mode 100644 index 0000000000000000000000000000000000000000..19332923713c98a57ea503f117dde72e5adb66bc GIT binary patch literal 81 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk~Bp9L@-6MgNgr|#RNCo5BBZeT+CWGJ8#gaB8 a?>@wM>t%i8`pqpFAhn*ZelF{r5}E*uW)$H7 literal 0 HcmV?d00001 diff --git a/Test/Assets/Textures/24bit.png b/Test/Assets/Textures/24bit.png new file mode 100644 index 0000000000000000000000000000000000000000..42a03a2d28d9aaf688eacca5ecec86d6ef6441f1 GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJwwYY*EWF_dAc};SokOZ@K=(!>c9iWB~t(Xvoow;`&VAYhwnMiBnD4cKbLh*2~7YT C+b!(? literal 0 HcmV?d00001 diff --git a/Test/Assets/Textures/32bit.png b/Test/Assets/Textures/32bit.png new file mode 100644 index 0000000000000000000000000000000000000000..6459a024e002b6e0aedffb664629e1033897789a GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJwwYY*EWF_dAc};SokOZ@K=;DO}N4!03;NSs56Z83KGlT>t<750n}OqaiRdLx5X(`$M45ISV`@iy0XB4ude`@%$AjKtah8 z*NBqf{Irtt#G+J&g2c?c61}|C5(N`I13g2_E7vxG6!CewIEH8hCrhw;G&C|YFbXg* TW`y*xgF@5O)z4*}Q$iB}L+&du literal 0 HcmV?d00001 diff --git a/Test/Framework/Graphics/Texture2DNonVisualTest.cs b/Test/Framework/Graphics/Texture2DNonVisualTest.cs index d93a195348b..5a93952cd49 100644 --- a/Test/Framework/Graphics/Texture2DNonVisualTest.cs +++ b/Test/Framework/Graphics/Texture2DNonVisualTest.cs @@ -18,15 +18,15 @@ internal class Texture2DNonVisualTest : GraphicsDeviceTestFixtureBase #if !XNA [TestCase("Assets/Textures/LogoOnly_64px.bmp")] -#if !DESKTOPGL - // not supported - [TestCase("Assets/Textures/LogoOnly_64px.tif")] - [TestCase("Assets/Textures/LogoOnly_64px.dds")] -#endif + [TestCase("Assets/Textures/LogoOnly_64px.tga")] #endif [TestCase("Assets/Textures/LogoOnly_64px.gif")] [TestCase("Assets/Textures/LogoOnly_64px.jpg")] [TestCase("Assets/Textures/LogoOnly_64px.png")] + [TestCase("Assets/Textures/1bit.png")] + [TestCase("Assets/Textures/8bit.png")] + [TestCase("Assets/Textures/24bit.png")] + [TestCase("Assets/Textures/32bit.png")] public void FromStreamShouldWorkTest(string filename) { using (System.IO.StreamReader reader = new System.IO.StreamReader(filename)) @@ -53,13 +53,11 @@ public void FromStreamShouldWorkTest(string filename) } #if XNA - [TestCase("Assets/Textures/LogoOnly_64px.bmp")] - [TestCase("Assets/Textures/LogoOnly_64px.dds")] - [TestCase("Assets/Textures/LogoOnly_64px.tif")] -#endif -#if !DESKTOPGL - [TestCase("Assets/Textures/LogoOnly_64px.tga")] + [TestCase("Assets/Textures/LogoOnly_64px.bmp")] #endif + // not supported + [TestCase("Assets/Textures/LogoOnly_64px.tif")] + [TestCase("Assets/Textures/LogoOnly_64px.dds")] [TestCase("Assets/Textures/SampleCube64DXT1Mips.dds")] public void FromStreamShouldFailTest(string filename) {