Skip to content

Commit

Permalink
Use StbSharp for all Texture2D.FromStream
Browse files Browse the repository at this point in the history
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 MonoGame#6001
  • Loading branch information
Steve Williams committed Feb 21, 2018
1 parent 46f90ee commit ff30e15
Show file tree
Hide file tree
Showing 10 changed files with 73 additions and 80 deletions.
33 changes: 9 additions & 24 deletions Build/Projects/MonoGame.Framework.definition
Original file line number Diff line number Diff line change
Expand Up @@ -1191,30 +1191,15 @@
<Compile Include="Utilities\Png\PngReader.cs" />
<Compile Include="Utilities\Png\PngWriter.cs" />
<Compile Include="Utilities\ByteBufferPool.cs" />
<Compile Include="Utilities\Imaging\Stb.Image.cs">
<Platforms>WindowsGL,Linux,MacOS</Platforms>
</Compile>
<Compile Include="Utilities\Imaging\Stb.Image.Generated.cs">
<Platforms>WindowsGL,Linux,MacOS</Platforms>
</Compile>
<Compile Include="Utilities\Imaging\ImageReader.cs">
<Platforms>WindowsGL,Linux,MacOS</Platforms>
</Compile>
<Compile Include="Utilities\Imaging\Stb.ImageWrite.cs">
<Platforms>WindowsGL,Linux,MacOS</Platforms>
</Compile>
<Compile Include="Utilities\Imaging\Stb.ImageWrite.Generated.cs">
<Platforms>WindowsGL,Linux,MacOS</Platforms>
</Compile>
<Compile Include="Utilities\Imaging\ImageWriter.cs">
<Platforms>WindowsGL,Linux,MacOS</Platforms>
</Compile>
<Compile Include="Utilities\Imaging\Operations.cs">
<Platforms>WindowsGL,Linux,MacOS</Platforms>
</Compile>
<Compile Include="Utilities\Imaging\PinnedArray.cs">
<Platforms>WindowsGL,Linux,MacOS</Platforms>
</Compile>
<Compile Include="Utilities\Imaging\Stb.Image.cs" />
<Compile Include="Utilities\Imaging\Stb.Image.Generated.cs" />
<Compile Include="Utilities\Imaging\ImageReader.cs" />
<Compile Include="Utilities\Imaging\Stb.ImageWrite.cs" />
<Compile Include="Utilities\Imaging\Stb.ImageWrite.Generated.cs" />
<Compile Include="Utilities\Imaging\ImageWriter.cs" />
<Compile Include="Utilities\Imaging\Operations.cs" />
<Compile Include="Utilities\Imaging\PinnedArray.cs" />




Expand Down
13 changes: 13 additions & 0 deletions Build/Projects/MonoGame.Tests.definition
Original file line number Diff line number Diff line change
Expand Up @@ -1334,6 +1334,19 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

<Content Include="Assets\Textures\1bit.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\Textures\8bit.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\Textures\24bit.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\Textures\32bit.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

<Content Include="Assets\Textures\blue_0.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
Expand Down
39 changes: 25 additions & 14 deletions MonoGame.Framework/Graphics/Texture2D.DirectX.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down
44 changes: 15 additions & 29 deletions MonoGame.Framework/Graphics/Texture2D.OpenGL.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
}

Expand All @@ -299,7 +286,6 @@ private static Texture2D PlatformFromStream(GraphicsDevice graphicsDevice, Strea
texture.SetData(data);

return texture;
#endif
}

#if IOS
Expand Down
4 changes: 2 additions & 2 deletions MonoGame.Framework/Utilities/Imaging/ImageReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -81,4 +81,4 @@ public byte[] Read(Stream stream, out int x, out int y, out int comp, int req_co
}
}
}
}
}
Binary file added Test/Assets/Textures/1bit.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Test/Assets/Textures/24bit.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Test/Assets/Textures/32bit.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Test/Assets/Textures/8bit.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 9 additions & 11 deletions Test/Framework/Graphics/Texture2DNonVisualTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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)
{
Expand Down

0 comments on commit ff30e15

Please sign in to comment.