Show More
Commit Description:
Various UI improvements.
Commit Description:
Various UI improvements.
File last commit:
Show/Diff file:
Action:
FNA/src/Graphics/Texture2D.cs
670 lines | 13.2 KiB | text/x-csharp | CSharpLexer
#region License
/* FNA - XNA4 Reimplementation for Desktop Platforms
* Copyright 2009-2020 Ethan Lee and the MonoGame Team
*
* 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!
if ( this is IRenderTarget &&
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 )
{
Format = SurfaceFormat.Color;
}
else
{
Format = format;
}
texture = GraphicsDevice.GLDevice.CreateTexture2D(
Format,
Width,
Height,
LevelCount,
(this is IRenderTarget)
);
}
#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");
}
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));
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
GraphicsDevice.GLDevice.SetTextureData2D(
texture,
Format,
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);
}
GraphicsDevice.GLDevice.SetTextureData2D(
texture,
Format,
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;
}
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
GraphicsDevice.GLDevice.GetTextureData2D(
texture,
Format,
Width >> level,
Height >> level,
level,
subX,
subY,
subW,
subH,
handle.AddrOfPinnedObject(),
startIndex,
elementCount,
Marshal.SizeOf(typeof(T))
);
handle.Free();
}
#endregion
#region Public Texture2D Save Methods
public void SaveAsJpeg(Stream stream, int width, int height)
{
// Get the Texture2D pixels
byte[] data = new byte[Width * Height * GetFormatSize(Format)];
GetData(data);
FNAPlatform.SaveJPG(
stream,
width,
height,
Width,
Height,
data
);
}
public void SaveAsPng(Stream stream, int width, int height)
{
// Get the Texture2D pixels
byte[] data = new byte[Width * Height * GetFormatSize(Format)];
GetData(data);
FNAPlatform.SavePNG(
stream,
width,
height,
Width,
Height,
data
);
}
#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);
}
// Read the image data from the stream
int width, height, len;
IntPtr pixels;
FNAPlatform.TextureDataFromStreamPtr(
stream,
out width,
out height,
out pixels,
out len
);
// Create the Texture2D from the raw pixel data
Texture2D result = new Texture2D(
graphicsDevice,
width,
height
);
result.SetDataPointerEXT(
0,
null,
pixels,
len
);
Marshal.FreeHGlobal(pixels);
return result;
}
public static Texture2D FromStream(
GraphicsDevice graphicsDevice,
Stream stream,
int width,
int height,
bool zoom
) {
// Read the image data from the stream
int realWidth, realHeight, len;
IntPtr pixels;
FNAPlatform.TextureDataFromStreamPtr(
stream,
out realWidth,
out realHeight,
out pixels,
out len,
width,
height,
zoom
);
// Create the Texture2D from the raw pixel data
Texture2D result = new Texture2D(
graphicsDevice,
realWidth,
realHeight
);
result.SetDataPointerEXT(
0,
null,
pixels,
len
);
Marshal.FreeHGlobal(pixels);
return result;
}
#endregion
#region Public Static Texture2D Extensions
/// <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
) {
FNAPlatform.TextureDataFromStream(
stream,
out width,
out height,
out pixels,
requestedWidth,
requestedHeight,
zoom
);
}
// DDS loading extension, based on MojoDDS
public static Texture2D DDSFromStreamEXT(
GraphicsDevice graphicsDevice,
Stream stream
) {
// A whole bunch of magic numbers, yay DDS!
const uint DDS_MAGIC = 0x20534444;
const uint DDS_HEADERSIZE = 124;
const uint DDS_PIXFMTSIZE = 32;
const uint DDSD_CAPS = 0x1;
const uint DDSD_HEIGHT = 0x2;
const uint DDSD_WIDTH = 0x4;
const uint DDSD_PITCH = 0x8;
const uint DDSD_FMT = 0x1000;
const uint DDSD_LINEARSIZE = 0x80000;
const uint DDSD_REQ = (
DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_FMT
);
const uint DDSCAPS_MIPMAP = 0x400000;
const uint DDSCAPS_TEXTURE = 0x1000;
const uint DDPF_FOURCC = 0x4;
const uint DDPF_RGB = 0x40;
const uint FOURCC_DXT1 = 0x31545844;
const uint FOURCC_DXT3 = 0x33545844;
const uint FOURCC_DXT5 = 0x35545844;
// const uint FOURCC_DX10 = 0x30315844;
const uint pitchAndLinear = (
DDSD_PITCH | DDSD_LINEARSIZE
);
Texture2D result;
// Begin BinaryReader, ignoring a tab!
using (BinaryReader reader = new BinaryReader(stream))
{
// File should start with 'DDS '
if (reader.ReadUInt32() != DDS_MAGIC)
{
return null;
}
// Texture info
uint size = reader.ReadUInt32();
if (size != DDS_HEADERSIZE)
{
return null;
}
uint flags = reader.ReadUInt32();
if ((flags & DDSD_REQ) != DDSD_REQ)
{
return null;
}
if ((flags & pitchAndLinear) == pitchAndLinear)
{
return null;
}
int height = reader.ReadInt32();
int width = reader.ReadInt32();
reader.ReadUInt32(); // dwPitchOrLinearSize, unused
reader.ReadUInt32(); // dwDepth, unused
int levels = reader.ReadInt32();
// "Reserved"
reader.ReadBytes(4 * 11);
// Format info
uint formatSize = reader.ReadUInt32();
if (formatSize != DDS_PIXFMTSIZE)
{
return null;
}
uint formatFlags = reader.ReadUInt32();
uint formatFourCC = reader.ReadUInt32();
uint formatRGBBitCount = reader.ReadUInt32();
uint formatRBitMask = reader.ReadUInt32();
uint formatGBitMask = reader.ReadUInt32();
uint formatBBitMask = reader.ReadUInt32();
uint formatABitMask = reader.ReadUInt32();
// dwCaps "stuff"
uint caps = reader.ReadUInt32();
if ((caps & DDSCAPS_TEXTURE) == 0)
{
return null;
}
uint caps2 = reader.ReadUInt32();
if (caps2 != 0)
{
return null;
}
reader.ReadUInt32(); // dwCaps3, unused
reader.ReadUInt32(); // dwCaps4, unused
// "Reserved"
reader.ReadUInt32();
// Mipmap sanity check
if ((caps & DDSCAPS_MIPMAP) != DDSCAPS_MIPMAP)
{
levels = 1;
}
// Determine texture format
SurfaceFormat format;
int levelSize;
int blockSize = 0;
if ((formatFlags & DDPF_FOURCC) == DDPF_FOURCC)
{
if (formatFourCC == FOURCC_DXT1)
{
format = SurfaceFormat.Dxt1;
blockSize = 8;
}
else if (formatFourCC == FOURCC_DXT3)
{
format = SurfaceFormat.Dxt3;
blockSize = 16;
}
else if (formatFourCC == FOURCC_DXT5)
{
format = SurfaceFormat.Dxt5;
blockSize = 16;
}
else
{
throw new NotSupportedException(
"Unsupported DDS texture format"
);
}
levelSize = (
((width > 0 ? ((width + 3) / 4) : 1) * blockSize) *
(height > 0 ? ((height + 3) / 4) : 1)
);
}
else if ((formatFlags & DDPF_RGB) == DDPF_RGB)
{
if ( formatRGBBitCount != 32 ||
formatRBitMask != 0x00FF0000 ||
formatGBitMask != 0x0000FF00 ||
formatBBitMask != 0x000000FF ||
formatABitMask != 0xFF000000 )
{
throw new NotSupportedException(
"Unsupported DDS texture format"
);
}
format = SurfaceFormat.ColorBgraEXT;
levelSize = (int) (
(((width * formatRGBBitCount) + 7) / 8) *
height
);
}
else
{
throw new NotSupportedException(
"Unsupported DDS texture format"
);
}
// Allocate/Load texture
result = new Texture2D(
graphicsDevice,
width,
height,
levels > 1,
format
);
byte[] tex = null;
if ( stream is MemoryStream &&
((MemoryStream) stream).TryGetBuffer(out tex) )
{
for (int i = 0; i < levels; i += 1)
{
result.SetData(
i,
null,
tex,
(int) stream.Seek(0, SeekOrigin.Current),
levelSize
);
stream.Seek(
levelSize,
SeekOrigin.Current
);
levelSize = Math.Max(
levelSize >> 2,
blockSize
);
}
}
else
{
for (int i = 0; i < levels; i += 1)
{
tex = reader.ReadBytes(levelSize);
result.SetData(
i,
null,
tex,
0,
tex.Length
);
levelSize = Math.Max(
levelSize >> 2,
blockSize
);
}
}
// End BinaryReader
}
// Finally.
return result;
}
#endregion
}
}