Show More
Commit Description:
Dialog tweaks and update swear list....
Commit Description:
Dialog tweaks and update swear list.
Keep the list of swears to censor up to date, lol.
References:
File last commit:
Show/Diff file:
Action:
FNA/src/Graphics/Texture2D.cs
616 lines | 11.6 KiB | text/x-csharp | CSharpLexer
616 lines | 11.6 KiB | text/x-csharp | CSharpLexer
r0 | #region License | |||
/* FNA - XNA4 Reimplementation for Desktop Platforms | ||||
r690 | * Copyright 2009-2022 Ethan Lee and the MonoGame Team | |||
r0 | * | |||
* Released under the Microsoft Public License. | ||||
* See LICENSE for details. | ||||
*/ | ||||
#endregion | ||||
#region Using Statements | ||||
using System; | ||||
using System.IO; | ||||
using System.Runtime.InteropServices; | ||||
#endregion | ||||
namespace Microsoft.Xna.Framework.Graphics | ||||
{ | ||||
public class Texture2D : Texture | ||||
{ | ||||
#region Public Properties | ||||
public int Width | ||||
{ | ||||
get; | ||||
private set; | ||||
} | ||||
public int Height | ||||
{ | ||||
get; | ||||
private set; | ||||
} | ||||
public Rectangle Bounds | ||||
{ | ||||
get | ||||
{ | ||||
return new Rectangle(0, 0, Width, Height); | ||||
} | ||||
} | ||||
#endregion | ||||
#region Public Constructors | ||||
public Texture2D( | ||||
GraphicsDevice graphicsDevice, | ||||
int width, | ||||
int height | ||||
) : this( | ||||
graphicsDevice, | ||||
width, | ||||
height, | ||||
false, | ||||
SurfaceFormat.Color | ||||
) { | ||||
} | ||||
public Texture2D( | ||||
GraphicsDevice graphicsDevice, | ||||
int width, | ||||
int height, | ||||
bool mipMap, | ||||
SurfaceFormat format | ||||
) { | ||||
if (graphicsDevice == null) | ||||
{ | ||||
throw new ArgumentNullException("graphicsDevice"); | ||||
} | ||||
GraphicsDevice = graphicsDevice; | ||||
Width = width; | ||||
Height = height; | ||||
LevelCount = mipMap ? CalculateMipLevels(width, height) : 1; | ||||
// TODO: Use QueryRenderTargetFormat! | ||||
r690 | if (this is IRenderTarget) | |||
r0 | { | |||
r690 | if (format == SurfaceFormat.ColorSrgbEXT) | |||
{ | ||||
if (FNA3D.FNA3D_SupportsSRGBRenderTargets(GraphicsDevice.GLDevice) == 0) | ||||
{ | ||||
// Renderable but not on this device | ||||
Format = SurfaceFormat.Color; | ||||
} | ||||
else | ||||
{ | ||||
Format = format; | ||||
} | ||||
} | ||||
else if ( format != SurfaceFormat.Color && | ||||
format != SurfaceFormat.Rgba1010102 && | ||||
format != SurfaceFormat.Rg32 && | ||||
format != SurfaceFormat.Rgba64 && | ||||
format != SurfaceFormat.Single && | ||||
format != SurfaceFormat.Vector2 && | ||||
format != SurfaceFormat.Vector4 && | ||||
format != SurfaceFormat.HalfSingle && | ||||
format != SurfaceFormat.HalfVector2 && | ||||
format != SurfaceFormat.HalfVector4 && | ||||
format != SurfaceFormat.HdrBlendable ) | ||||
{ | ||||
// Not a renderable format period | ||||
Format = SurfaceFormat.Color; | ||||
} | ||||
else | ||||
{ | ||||
Format = format; | ||||
} | ||||
r0 | } | |||
else | ||||
{ | ||||
Format = format; | ||||
} | ||||
r690 | texture = FNA3D.FNA3D_CreateTexture2D( | |||
GraphicsDevice.GLDevice, | ||||
r0 | Format, | |||
Width, | ||||
Height, | ||||
LevelCount, | ||||
r690 | (byte) ((this is IRenderTarget) ? 1 : 0) | |||
r0 | ); | |||
} | ||||
#endregion | ||||
#region Public SetData Methods | ||||
public void SetData<T>(T[] data) where T : struct | ||||
{ | ||||
SetData( | ||||
0, | ||||
null, | ||||
data, | ||||
0, | ||||
data.Length | ||||
); | ||||
} | ||||
public void SetData<T>( | ||||
T[] data, | ||||
int startIndex, | ||||
int elementCount | ||||
) where T : struct { | ||||
SetData( | ||||
0, | ||||
null, | ||||
data, | ||||
startIndex, | ||||
elementCount | ||||
); | ||||
} | ||||
public void SetData<T>( | ||||
int level, | ||||
Rectangle? rect, | ||||
T[] data, | ||||
int startIndex, | ||||
int elementCount | ||||
) where T : struct { | ||||
if (data == null) | ||||
{ | ||||
throw new ArgumentNullException("data"); | ||||
} | ||||
r690 | if (startIndex < 0) | |||
{ | ||||
throw new ArgumentOutOfRangeException("startIndex"); | ||||
} | ||||
if (data.Length < (elementCount + startIndex)) | ||||
{ | ||||
throw new ArgumentOutOfRangeException("elementCount"); | ||||
} | ||||
r0 | ||||
int x, y, w, h; | ||||
if (rect.HasValue) | ||||
{ | ||||
x = rect.Value.X; | ||||
y = rect.Value.Y; | ||||
w = rect.Value.Width; | ||||
h = rect.Value.Height; | ||||
} | ||||
else | ||||
{ | ||||
x = 0; | ||||
y = 0; | ||||
w = Math.Max(Width >> level, 1); | ||||
h = Math.Max(Height >> level, 1); | ||||
} | ||||
int elementSize = Marshal.SizeOf(typeof(T)); | ||||
r690 | int requiredBytes = (w * h * GetFormatSize(Format)) / GetBlockSizeSquared(Format); | |||
int availableBytes = elementCount * elementSize; | ||||
if (requiredBytes > availableBytes) | ||||
{ | ||||
throw new ArgumentOutOfRangeException("rect", "The region you are trying to upload is larger than the amount of data you provided."); | ||||
} | ||||
r0 | GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); | |||
r690 | FNA3D.FNA3D_SetTextureData2D( | |||
GraphicsDevice.GLDevice, | ||||
r0 | texture, | |||
x, | ||||
y, | ||||
w, | ||||
h, | ||||
level, | ||||
handle.AddrOfPinnedObject() + startIndex * elementSize, | ||||
elementCount * elementSize | ||||
); | ||||
handle.Free(); | ||||
} | ||||
public void SetDataPointerEXT( | ||||
int level, | ||||
Rectangle? rect, | ||||
IntPtr data, | ||||
int dataLength | ||||
) { | ||||
if (data == IntPtr.Zero) | ||||
{ | ||||
throw new ArgumentNullException("data"); | ||||
} | ||||
int x, y, w, h; | ||||
if (rect.HasValue) | ||||
{ | ||||
x = rect.Value.X; | ||||
y = rect.Value.Y; | ||||
w = rect.Value.Width; | ||||
h = rect.Value.Height; | ||||
} | ||||
else | ||||
{ | ||||
x = 0; | ||||
y = 0; | ||||
w = Math.Max(Width >> level, 1); | ||||
h = Math.Max(Height >> level, 1); | ||||
} | ||||
r690 | FNA3D.FNA3D_SetTextureData2D( | |||
GraphicsDevice.GLDevice, | ||||
r0 | texture, | |||
x, | ||||
y, | ||||
w, | ||||
h, | ||||
level, | ||||
data, | ||||
dataLength | ||||
); | ||||
} | ||||
#endregion | ||||
#region Public GetData Methods | ||||
public void GetData<T>(T[] data) where T : struct | ||||
{ | ||||
GetData( | ||||
0, | ||||
null, | ||||
data, | ||||
0, | ||||
data.Length | ||||
); | ||||
} | ||||
public void GetData<T>( | ||||
T[] data, | ||||
int startIndex, | ||||
int elementCount | ||||
) where T : struct { | ||||
GetData( | ||||
0, | ||||
null, | ||||
data, | ||||
startIndex, | ||||
elementCount | ||||
); | ||||
} | ||||
public void GetData<T>( | ||||
int level, | ||||
Rectangle? rect, | ||||
T[] data, | ||||
int startIndex, | ||||
int elementCount | ||||
) where T : struct { | ||||
if (data == null || data.Length == 0) | ||||
{ | ||||
throw new ArgumentException("data cannot be null"); | ||||
} | ||||
if (data.Length < startIndex + elementCount) | ||||
{ | ||||
throw new ArgumentException( | ||||
"The data passed has a length of " + data.Length.ToString() + | ||||
" but " + elementCount.ToString() + " pixels have been requested." | ||||
); | ||||
} | ||||
int subX, subY, subW, subH; | ||||
if (rect == null) | ||||
{ | ||||
subX = 0; | ||||
subY = 0; | ||||
subW = Width >> level; | ||||
subH = Height >> level; | ||||
} | ||||
else | ||||
{ | ||||
subX = rect.Value.X; | ||||
subY = rect.Value.Y; | ||||
subW = rect.Value.Width; | ||||
subH = rect.Value.Height; | ||||
} | ||||
r690 | int elementSizeInBytes = Marshal.SizeOf(typeof(T)); | |||
ValidateGetDataFormat(Format, elementSizeInBytes); | ||||
r0 | GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); | |||
r690 | FNA3D.FNA3D_GetTextureData2D( | |||
GraphicsDevice.GLDevice, | ||||
r0 | texture, | |||
subX, | ||||
subY, | ||||
subW, | ||||
subH, | ||||
r690 | level, | |||
handle.AddrOfPinnedObject() + (startIndex * elementSizeInBytes), | ||||
elementCount * elementSizeInBytes | ||||
r0 | ); | |||
handle.Free(); | ||||
} | ||||
#endregion | ||||
#region Public Texture2D Save Methods | ||||
public void SaveAsJpeg(Stream stream, int width, int height) | ||||
{ | ||||
r690 | int quality; | |||
string qualityString = Environment.GetEnvironmentVariable("FNA_GRAPHICS_JPEG_SAVE_QUALITY"); | ||||
if (string.IsNullOrEmpty(qualityString) || !int.TryParse(qualityString, out quality)) | ||||
{ | ||||
quality = 100; // FIXME: What does XNA pick for quality? -flibit | ||||
} | ||||
int len = Width * Height * GetFormatSize(Format); | ||||
IntPtr data = Marshal.AllocHGlobal(len); | ||||
FNA3D.FNA3D_GetTextureData2D( | ||||
GraphicsDevice.GLDevice, | ||||
texture, | ||||
0, | ||||
0, | ||||
Width, | ||||
height, | ||||
0, | ||||
data, | ||||
len | ||||
); | ||||
FNA3D.WriteJPGStream( | ||||
r0 | stream, | |||
r690 | Width, | |||
Height, | ||||
r0 | width, | |||
height, | ||||
r690 | data, | |||
quality | ||||
r0 | ); | |||
r690 | ||||
Marshal.FreeHGlobal(data); | ||||
r0 | } | |||
public void SaveAsPng(Stream stream, int width, int height) | ||||
{ | ||||
r690 | int len = Width * Height * GetFormatSize(Format); | |||
IntPtr data = Marshal.AllocHGlobal(len); | ||||
FNA3D.FNA3D_GetTextureData2D( | ||||
GraphicsDevice.GLDevice, | ||||
texture, | ||||
0, | ||||
0, | ||||
Width, | ||||
height, | ||||
0, | ||||
data, | ||||
len | ||||
); | ||||
FNA3D.WritePNGStream( | ||||
r0 | stream, | |||
r690 | Width, | |||
Height, | ||||
r0 | width, | |||
height, | ||||
data | ||||
); | ||||
r690 | ||||
Marshal.FreeHGlobal(data); | ||||
r0 | } | |||
#endregion | ||||
#region Public Static Texture2D Load Methods | ||||
public static Texture2D FromStream(GraphicsDevice graphicsDevice, Stream stream) | ||||
{ | ||||
if (stream.CanSeek && stream.Position == stream.Length) | ||||
{ | ||||
stream.Seek(0, SeekOrigin.Begin); | ||||
} | ||||
int width, height, len; | ||||
r690 | IntPtr pixels = FNA3D.ReadImageStream( | |||
r0 | stream, | |||
out width, | ||||
out height, | ||||
out len | ||||
); | ||||
Texture2D result = new Texture2D( | ||||
graphicsDevice, | ||||
width, | ||||
height | ||||
); | ||||
result.SetDataPointerEXT( | ||||
0, | ||||
null, | ||||
pixels, | ||||
len | ||||
); | ||||
r690 | ||||
FNA3D.FNA3D_Image_Free(pixels); | ||||
r0 | return result; | |||
} | ||||
public static Texture2D FromStream( | ||||
GraphicsDevice graphicsDevice, | ||||
Stream stream, | ||||
int width, | ||||
int height, | ||||
bool zoom | ||||
) { | ||||
r690 | if (stream.CanSeek && stream.Position == stream.Length) | |||
{ | ||||
stream.Seek(0, SeekOrigin.Begin); | ||||
} | ||||
r0 | int realWidth, realHeight, len; | |||
r690 | IntPtr pixels = FNA3D.ReadImageStream( | |||
r0 | stream, | |||
out realWidth, | ||||
out realHeight, | ||||
out len, | ||||
width, | ||||
height, | ||||
zoom | ||||
); | ||||
Texture2D result = new Texture2D( | ||||
graphicsDevice, | ||||
realWidth, | ||||
realHeight | ||||
); | ||||
result.SetDataPointerEXT( | ||||
0, | ||||
null, | ||||
pixels, | ||||
len | ||||
); | ||||
r690 | ||||
FNA3D.FNA3D_Image_Free(pixels); | ||||
r0 | return result; | |||
} | ||||
#endregion | ||||
#region Public Static Texture2D Extensions | ||||
r690 | ||||
r0 | /// <summary> | |||
/// Loads image data from a given stream. | ||||
/// </summary> | ||||
/// <remarks> | ||||
/// This is an extension of XNA 4 and is not compatible with XNA. It exists to help with dynamically reloading | ||||
/// textures while games are running. Games can use this method to read a stream into memory and then call | ||||
/// SetData on a texture with that data, rather than having to dispose the texture and recreate it entirely. | ||||
/// </remarks> | ||||
/// <param name="stream">The stream from which to read the image data.</param> | ||||
/// <param name="width">Outputs the width of the image.</param> | ||||
/// <param name="height">Outputs the height of the image.</param> | ||||
/// <param name="pixels">Outputs the pixel data of the image, in non-premultiplied RGBA format.</param> | ||||
/// <param name="requestedWidth">Preferred width of the resulting image data</param> | ||||
/// <param name="requestedHeight">Preferred height of the resulting image data</param> | ||||
/// <param name="zoom">false to maintain aspect ratio, true to crop image</param> | ||||
public static void TextureDataFromStreamEXT( | ||||
Stream stream, | ||||
out int width, | ||||
out int height, | ||||
out byte[] pixels, | ||||
int requestedWidth = -1, | ||||
int requestedHeight = -1, | ||||
bool zoom = false | ||||
) { | ||||
r690 | if (stream.CanSeek && stream.Position == stream.Length) | |||
{ | ||||
stream.Seek(0, SeekOrigin.Begin); | ||||
} | ||||
int len; | ||||
IntPtr pixPtr = FNA3D.ReadImageStream( | ||||
r0 | stream, | |||
out width, | ||||
out height, | ||||
r690 | out len, | |||
r0 | requestedWidth, | |||
requestedHeight, | ||||
zoom | ||||
); | ||||
r690 | ||||
pixels = new byte[len]; | ||||
Marshal.Copy(pixPtr, pixels, 0, len); | ||||
FNA3D.FNA3D_Image_Free(pixPtr); | ||||
r0 | } | |||
public static Texture2D DDSFromStreamEXT( | ||||
GraphicsDevice graphicsDevice, | ||||
Stream stream | ||||
) { | ||||
Texture2D result; | ||||
// Begin BinaryReader, ignoring a tab! | ||||
using (BinaryReader reader = new BinaryReader(stream)) | ||||
{ | ||||
r690 | int width, height, levels; | |||
bool isCube; | ||||
SurfaceFormat format; | ||||
Texture.ParseDDS( | ||||
reader, | ||||
out format, | ||||
out width, | ||||
out height, | ||||
out levels, | ||||
out isCube | ||||
); | ||||
r0 | ||||
r690 | if (isCube) | |||
r0 | { | |||
r690 | throw new FormatException("This file contains cube map data!"); | |||
r0 | } | |||
// Allocate/Load texture | ||||
result = new Texture2D( | ||||
graphicsDevice, | ||||
width, | ||||
height, | ||||
levels > 1, | ||||
format | ||||
); | ||||
r690 | ||||
r0 | byte[] tex = null; | |||
if ( stream is MemoryStream && | ||||
((MemoryStream) stream).TryGetBuffer(out tex) ) | ||||
{ | ||||
for (int i = 0; i < levels; i += 1) | ||||
{ | ||||
r690 | int levelSize = Texture.CalculateDDSLevelSize( | |||
width >> i, | ||||
height >> i, | ||||
format | ||||
); | ||||
r0 | result.SetData( | |||
i, | ||||
null, | ||||
tex, | ||||
(int) stream.Seek(0, SeekOrigin.Current), | ||||
levelSize | ||||
); | ||||
stream.Seek( | ||||
levelSize, | ||||
SeekOrigin.Current | ||||
); | ||||
} | ||||
} | ||||
else | ||||
{ | ||||
for (int i = 0; i < levels; i += 1) | ||||
{ | ||||
r690 | tex = reader.ReadBytes(Texture.CalculateDDSLevelSize( | |||
width >> i, | ||||
height >> i, | ||||
format | ||||
)); | ||||
r0 | result.SetData( | |||
i, | ||||
null, | ||||
tex, | ||||
0, | ||||
tex.Length | ||||
); | ||||
} | ||||
} | ||||
// End BinaryReader | ||||
} | ||||
// Finally. | ||||
return result; | ||||
} | ||||
#endregion | ||||
} | ||||
} | ||||