I currently have an image processing application that outputs processed images post pixel shaders have been applied. However, using the examples I've found on the internet, the colors are flip to BGRA when RGBA is expected. I currently work around this with by post export processing to get the desired RGBA format. I was hoping someone else has dealt with this and has a solution that does not require post processing to correct the format.
1 Answer
I have two methods for doing this I will post them both.
Code is in VB.net and using sharpDX 3.0.2
Basically you copy your texture data to a staging resource and then write that staging resource to disk using WIC.
If I recall I may have pulled this out of the SharpDX.Toolkit, DrawContext is just a wrapper around Device context so it should just be a name change and it will work.
This will save textures to disk as DDS files, you can change this by changing out the WIC encoder or load the images into Photoshop and do it from there.
' jpeg Container Format GUID: 19e4a5aa-5662-4fc5-a0c01758028e1057
' PNG Container Format GUID: 1b7cfaf4-713f-473c-bbcd6137425faeaf
Dim blah As Guid = Guid.Parse("9967cb952E854ac88ca283d7ccd425c9") 'this is the WIC DDS Container Format GUID
Dim bitmapEncoder As DdsEncoder = New DdsEncoder 'This is the WIC DDS encoder, try changing this to png or jpeg.
I like the DDS file format so I just stick with that.
How to save a texture to a File
Dim fs As New FileStream(DirectXDevice.ContentPath & "Textures\Layer1.dds", FileMode.OpenOrCreate)
SaveTexture(_Context, TerrainLayerBuffer, fs)
fs.Flush()
fs.Dispose()
How to save a texture to a System.Drawing.Image
Dim ms As New MemoryStream()
SaveTexture(_Context, TerrainLayerBuffer, ms)
ms.Flush()
Dim newimage As System.Drawing.Image = System.Drawing.Image.FromStream(ms)
ms.Dispose()
The save texture methods
Public Sub SaveTexture(_device As DrawContext, res As Resource,ByRef stream As Stream)
Dim factory As New ImagingFactory()
Dim texture = TryCast(res, Texture2D)
'Setup Staging resource
Dim textureCopy = New Texture2D(_device.Device, New Texture2DDescription() With {
.Width = CType(texture.Description.Width, Integer),
.Height = CType(texture.Description.Height, Integer),
.MipLevels = 1,
.ArraySize = 1,
.Format = texture.Description.Format,
.Usage = ResourceUsage.Staging,
.SampleDescription = New SharpDX.DXGI.SampleDescription(1, 0),
.BindFlags = BindFlags.None,
.CpuAccessFlags = CpuAccessFlags.Read,
.OptionFlags = ResourceOptionFlags.None
})
Copy Texture Data To Staging Resource
_device.CopyResource(res, textureCopy)
'Read Staging resource data to a Datastream to be passed to the WIC codec
Dim dataStream As DataStream
Dim dataBox = _device.MapSubresource(textureCopy, 0, 0, MapMode.Read, Direct3D11.MapFlags.None, dataStream)
Dim dataRectangle = New DataRectangle() With {
.DataPointer = dataStream.DataPointer,
.Pitch = dataBox.RowPitch
}
' TODO: should use some conversion from textureCopy.Description.Format
Dim bitmap As BitmapSource = New Bitmap(factory, textureCopy.Description.Width, textureCopy.Description.Height, PixelFormatFromFormat(textureCopy.Description.Format), dataRectangle)
'Set Up WIC
Using WICStream As New WICStream(factory, stream)
Dim blah As Guid = Guid.Parse("9967cb952E854ac88ca283d7ccd425c9") 'DDS Container Format
Dim bitmapEncoder As BitmapEncoder = New BitmapEncoder(factory, blah, WICStream)
Dim ccc As BitmapEncoderInfo = bitmapEncoder.EncoderInfo
If bitmapEncoder IsNot Nothing Then
Using bitmapFrameEncode = New BitmapFrameEncode(bitmapEncoder)
bitmapFrameEncode.Initialize()
bitmapFrameEncode.SetSize(bitmap.Size.Width, bitmap.Size.Height)
bitmapFrameEncode.SetPixelFormat(PixelFormatFromFormat(textureCopy.Description.Format))
bitmapFrameEncode.WriteSource(bitmap)
bitmapFrameEncode.Commit()
bitmapEncoder.Commit()
End Using
bitmapEncoder.Dispose()
End If
End Using
_device.UnmapSubresource(textureCopy, 0)
"Clean UP
textureCopy.Dispose()
bitmap.Dispose()
factory.Dispose()
End Sub
Public Sub SaveTexture(_device As DrawContext, res As Resource, _filename As String)
Dim factory As New ImagingFactory()
Dim texture = TryCast(res, Texture2D)
Dim textureCopy = New Texture2D(_device.Device, New Texture2DDescription() With {
.Width = CType(texture.Description.Width, Integer),
.Height = CType(texture.Description.Height, Integer),
.MipLevels = 1,
.ArraySize = 1,
.Format = texture.Description.Format,
.Usage = ResourceUsage.Staging,
.SampleDescription = New SharpDX.DXGI.SampleDescription(1, 0),
.BindFlags = BindFlags.None,
.CpuAccessFlags = CpuAccessFlags.Read,
.OptionFlags = ResourceOptionFlags.None
})
_device.CopyResource(res, textureCopy)
Dim dataStream As DataStream
Dim dataBox = _device.MapSubresource(textureCopy, 0, 0, MapMode.Read, Direct3D11.MapFlags.None, dataStream)
Dim dataRectangle = New DataRectangle() With {
.DataPointer = dataStream.DataPointer,
.Pitch = dataBox.RowPitch
}
' TODO: should use some conversion from textureCopy.Description.Format
Dim bitmap = New Bitmap(factory, textureCopy.Description.Width, textureCopy.Description.Height, PixelFormatFromFormat(textureCopy.Description.Format), dataRectangle)
Using WICStream = New WICStream(factory, _filename, IO.NativeFileAccess.ReadWrite)
Dim bitmapEncoder As DdsEncoder = New DdsEncoder(WICStream.NativePointer)
If bitmapEncoder IsNot Nothing Then
Using bitmapFrameEncode = New BitmapFrameEncode(bitmapEncoder)
bitmapFrameEncode.Initialize()
bitmapFrameEncode.SetSize(bitmap.Size.Width, bitmap.Size.Height)
bitmapFrameEncode.SetPixelFormat(PixelFormatFromFormat(textureCopy.Description.Format))
bitmapFrameEncode.WriteSource(bitmap)
bitmapFrameEncode.Commit()
bitmapEncoder.CreateNewFrame(bitmapFrameEncode, 0, 0, 0)
End Using
bitmapEncoder.Dispose()
End If
End Using
_device.UnmapSubresource(textureCopy, 0)
textureCopy.Dispose()
bitmap.Dispose()
factory.Dispose()
factory = Nothing
bitmap = Nothing
textureCopy = Nothing
End Sub
Public Function PixelFormatFromFormat(format As SharpDX.DXGI.Format) As Guid
Select Case format
Case SharpDX.DXGI.Format.R32G32B32A32_Typeless, SharpDX.DXGI.Format.R32G32B32A32_Float
Return SharpDX.WIC.PixelFormat.Format128bppRGBAFloat
Case SharpDX.DXGI.Format.R32G32B32A32_UInt, SharpDX.DXGI.Format.R32G32B32A32_SInt
Return SharpDX.WIC.PixelFormat.Format128bppRGBAFixedPoint
Case SharpDX.DXGI.Format.R32G32B32_Typeless, SharpDX.DXGI.Format.R32G32B32_Float
Return SharpDX.WIC.PixelFormat.Format96bppRGBFloat
Case SharpDX.DXGI.Format.R32G32B32_UInt, SharpDX.DXGI.Format.R32G32B32_SInt
Return SharpDX.WIC.PixelFormat.Format96bppRGBFixedPoint
Case SharpDX.DXGI.Format.R16G16B16A16_Typeless, SharpDX.DXGI.Format.R16G16B16A16_Float, SharpDX.DXGI.Format.R16G16B16A16_UNorm, SharpDX.DXGI.Format.R16G16B16A16_UInt, SharpDX.DXGI.Format.R16G16B16A16_SNorm, SharpDX.DXGI.Format.R16G16B16A16_SInt
Return SharpDX.WIC.PixelFormat.Format64bppRGBA
Case SharpDX.DXGI.Format.R32G32_Typeless, SharpDX.DXGI.Format.R32G32_Float, SharpDX.DXGI.Format.R32G32_UInt, SharpDX.DXGI.Format.R32G32_SInt, SharpDX.DXGI.Format.R32G8X24_Typeless, SharpDX.DXGI.Format.D32_Float_S8X24_UInt,
SharpDX.DXGI.Format.R32_Float_X8X24_Typeless, SharpDX.DXGI.Format.X32_Typeless_G8X24_UInt
Return Guid.Empty
Case SharpDX.DXGI.Format.R10G10B10A2_Typeless, SharpDX.DXGI.Format.R10G10B10A2_UNorm, SharpDX.DXGI.Format.R10G10B10A2_UInt
Return SharpDX.WIC.PixelFormat.Format32bppRGBA1010102
Case SharpDX.DXGI.Format.R11G11B10_Float
Return Guid.Empty
Case SharpDX.DXGI.Format.R8G8B8A8_Typeless, SharpDX.DXGI.Format.R8G8B8A8_UNorm, SharpDX.DXGI.Format.R8G8B8A8_UNorm_SRgb, SharpDX.DXGI.Format.R8G8B8A8_UInt, SharpDX.DXGI.Format.R8G8B8A8_SNorm, SharpDX.DXGI.Format.R8G8B8A8_SInt
Return SharpDX.WIC.PixelFormat.Format32bppRGBA
Case SharpDX.DXGI.Format.R16G16_Typeless, SharpDX.DXGI.Format.R16G16_Float, SharpDX.DXGI.Format.R16G16_UNorm, SharpDX.DXGI.Format.R16G16_UInt, SharpDX.DXGI.Format.R16G16_SNorm, SharpDX.DXGI.Format.R16G16_SInt
Return Guid.Empty
Case SharpDX.DXGI.Format.R32_Typeless, SharpDX.DXGI.Format.D32_Float, SharpDX.DXGI.Format.R32_Float, SharpDX.DXGI.Format.R32_UInt, SharpDX.DXGI.Format.R32_SInt
Return Guid.Empty
Case SharpDX.DXGI.Format.R24G8_Typeless, SharpDX.DXGI.Format.D24_UNorm_S8_UInt, SharpDX.DXGI.Format.R24_UNorm_X8_Typeless
Return SharpDX.WIC.PixelFormat.Format32bppGrayFloat
Case SharpDX.DXGI.Format.X24_Typeless_G8_UInt, SharpDX.DXGI.Format.R9G9B9E5_Sharedexp, SharpDX.DXGI.Format.R8G8_B8G8_UNorm, SharpDX.DXGI.Format.G8R8_G8B8_UNorm
Return Guid.Empty
Case SharpDX.DXGI.Format.B8G8R8A8_UNorm, SharpDX.DXGI.Format.B8G8R8X8_UNorm
Return SharpDX.WIC.PixelFormat.Format32bppBGRA
Case SharpDX.DXGI.Format.R10G10B10_Xr_Bias_A2_UNorm
Return SharpDX.WIC.PixelFormat.Format32bppBGR101010
Case SharpDX.DXGI.Format.B8G8R8A8_Typeless, SharpDX.DXGI.Format.B8G8R8A8_UNorm_SRgb, SharpDX.DXGI.Format.B8G8R8X8_Typeless, SharpDX.DXGI.Format.B8G8R8X8_UNorm_SRgb
Return SharpDX.WIC.PixelFormat.Format32bppBGRA
Case SharpDX.DXGI.Format.R8G8_Typeless, SharpDX.DXGI.Format.R8G8_UNorm, SharpDX.DXGI.Format.R8G8_UInt, SharpDX.DXGI.Format.R8G8_SNorm, SharpDX.DXGI.Format.R8G8_SInt
Return Guid.Empty
Case SharpDX.DXGI.Format.R16_Typeless, SharpDX.DXGI.Format.R16_Float, SharpDX.DXGI.Format.D16_UNorm, SharpDX.DXGI.Format.R16_UNorm, SharpDX.DXGI.Format.R16_SNorm
Return SharpDX.WIC.PixelFormat.Format16bppGrayHalf
Case SharpDX.DXGI.Format.R16_UInt, SharpDX.DXGI.Format.R16_SInt
Return SharpDX.WIC.PixelFormat.Format16bppGrayFixedPoint
Case SharpDX.DXGI.Format.B5G6R5_UNorm
Return SharpDX.WIC.PixelFormat.Format16bppBGR565
Case SharpDX.DXGI.Format.B5G5R5A1_UNorm
Return SharpDX.WIC.PixelFormat.Format16bppBGRA5551
Case SharpDX.DXGI.Format.B4G4R4A4_UNorm
Return Guid.Empty
Case SharpDX.DXGI.Format.R8_Typeless, SharpDX.DXGI.Format.R8_UNorm, SharpDX.DXGI.Format.R8_UInt, SharpDX.DXGI.Format.R8_SNorm, SharpDX.DXGI.Format.R8_SInt
Return SharpDX.WIC.PixelFormat.Format8bppGray
Case SharpDX.DXGI.Format.A8_UNorm
Return SharpDX.WIC.PixelFormat.Format8bppAlpha
Case SharpDX.DXGI.Format.R1_UNorm
Return SharpDX.WIC.PixelFormat.Format1bppIndexed
Case Else
Return Guid.Empty
End Select
End Function
-
\$\begingroup\$ I need to return the image as a "System.Drawing.Image." I'm not able to save the image, it must stay in memory. So I need to get from the SharpDX Bitmap to Image. \$\endgroup\$– ReaperCommented Jan 19, 2017 at 1:07
-
\$\begingroup\$ rather than use a file stream change it to a memory stream and the call image.fromstream. Ill update my answer if you need \$\endgroup\$ Commented Jan 19, 2017 at 5:31
-
\$\begingroup\$ Updated my answer only thing is I'm not sure if a system.drawing.image can load a .DDS file so you will need to play with the texture saving methods.... I'm going over my code now so I should have a bit more of a working in code solution soon'ish. \$\endgroup\$ Commented Jan 19, 2017 at 5:46
-
\$\begingroup\$ Now that I've added this to production. It is not fast enough. Is there a faster method? \$\endgroup\$– ReaperCommented Apr 27, 2017 at 18:12
-
\$\begingroup\$ You could write your own importer and exporter. Take a look at DevIL its an image lib that may help. Ill have a tinker after school and see what I can come up with. Can I ask how slow? in say milliseconds? Because there may not be a faster way to do it in DotNet. System.Drawing.Image is not really meant for this kind of thing so that would most likely be the problem. \$\endgroup\$ Commented Apr 27, 2017 at 20:34