Files
LuaCsForBarotraumaEP/Libraries/MonoGame.Framework/Src/MonoGame.Framework.Content.Pipeline/Graphics/DxtBitmapContent.cs
2019-06-25 16:00:44 +03:00

233 lines
8.4 KiB
C#

// MonoGame - Copyright (C) The MonoGame Team
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
using System;
using Microsoft.Xna.Framework.Graphics;
using Nvidia.TextureTools;
using System.Runtime.InteropServices;
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
{
public abstract class DxtBitmapContent : BitmapContent
{
private byte[] _bitmapData;
private int _blockSize;
private SurfaceFormat _format;
private int _nvttWriteOffset;
protected DxtBitmapContent(int blockSize)
{
if (!((blockSize == 8) || (blockSize == 16)))
throw new ArgumentException("Invalid block size");
_blockSize = blockSize;
TryGetFormat(out _format);
}
protected DxtBitmapContent(int blockSize, int width, int height)
: this(blockSize)
{
Width = width;
Height = height;
}
public override byte[] GetPixelData()
{
return _bitmapData;
}
public override void SetPixelData(byte[] sourceData)
{
_bitmapData = sourceData;
}
private void NvttBeginImage(int size, int width, int height, int depth, int face, int miplevel)
{
_bitmapData = new byte[size];
_nvttWriteOffset = 0;
}
private bool NvttWriteImage(IntPtr data, int length)
{
Marshal.Copy(data, _bitmapData, _nvttWriteOffset, length);
_nvttWriteOffset += length;
return true;
}
private void NvttEndImage()
{
}
private static void PrepareNVTT(byte[] data)
{
for (var x = 0; x < data.Length; x += 4)
{
// NVTT wants BGRA where our source is RGBA so
// we swap the red and blue channels.
data[x] ^= data[x + 2];
data[x + 2] ^= data[x];
data[x] ^= data[x + 2];
}
}
private static void PrepareNVTT_DXT1(byte[] data, out bool hasTransparency)
{
hasTransparency = false;
for (var x = 0; x < data.Length; x += 4)
{
// NVTT wants BGRA where our source is RGBA so
// we swap the red and blue channels.
data[x] ^= data[x + 2];
data[x + 2] ^= data[x];
data[x] ^= data[x + 2];
// Look for non-opaque pixels.
var alpha = data[x + 3];
if (alpha < 255)
hasTransparency = true;
}
}
protected override bool TryCopyFrom(BitmapContent sourceBitmap, Rectangle sourceRegion, Rectangle destinationRegion)
{
SurfaceFormat sourceFormat;
if (!sourceBitmap.TryGetFormat(out sourceFormat))
return false;
SurfaceFormat format;
TryGetFormat(out format);
// A shortcut for copying the entire bitmap to another bitmap of the same type and format
if (format == sourceFormat && (sourceRegion == new Rectangle(0, 0, Width, Height)) && sourceRegion == destinationRegion)
{
SetPixelData(sourceBitmap.GetPixelData());
return true;
}
// TODO: Add a XNA unit test to see what it does
// my guess is that this is invalid for DXT.
//
// Destination region copy is not yet supported
if (destinationRegion != new Rectangle(0, 0, Width, Height))
return false;
// If the source is not Vector4 or requires resizing, send it through BitmapContent.Copy
if (!(sourceBitmap is PixelBitmapContent<Vector4>) || sourceRegion.Width != destinationRegion.Width || sourceRegion.Height != destinationRegion.Height)
{
try
{
BitmapContent.Copy(sourceBitmap, sourceRegion, this, destinationRegion);
return true;
}
catch (InvalidOperationException)
{
return false;
}
}
// NVTT wants 8bit data in BGRA format.
var colorBitmap = new PixelBitmapContent<Color>(sourceBitmap.Width, sourceBitmap.Height);
BitmapContent.Copy(sourceBitmap, colorBitmap);
var sourceData = colorBitmap.GetPixelData();
var dataHandle = GCHandle.Alloc(sourceData, GCHandleType.Pinned);
AlphaMode alphaMode;
Format outputFormat;
var alphaDither = false;
switch (format)
{
case SurfaceFormat.Dxt1:
case SurfaceFormat.Dxt1SRgb:
{
bool hasTransparency;
PrepareNVTT_DXT1(sourceData, out hasTransparency);
outputFormat = hasTransparency ? Format.DXT1a : Format.DXT1;
alphaMode = hasTransparency ? AlphaMode.Transparency : AlphaMode.None;
alphaDither = true;
break;
}
case SurfaceFormat.Dxt3:
case SurfaceFormat.Dxt3SRgb:
{
PrepareNVTT(sourceData);
outputFormat = Format.DXT3;
alphaMode = AlphaMode.Transparency;
break;
}
case SurfaceFormat.Dxt5:
case SurfaceFormat.Dxt5SRgb:
{
PrepareNVTT(sourceData);
outputFormat = Format.DXT5;
alphaMode = AlphaMode.Transparency;
break;
}
default:
throw new InvalidOperationException("Invalid DXT surface format!");
}
// Do all the calls to the NVTT wrapper within this handler
// so we properly clean up if things blow up.
try
{
var dataPtr = dataHandle.AddrOfPinnedObject();
var inputOptions = new InputOptions();
inputOptions.SetTextureLayout(TextureType.Texture2D, colorBitmap.Width, colorBitmap.Height, 1);
inputOptions.SetMipmapData(dataPtr, colorBitmap.Width, colorBitmap.Height, 1, 0, 0);
inputOptions.SetMipmapGeneration(false);
inputOptions.SetGamma(1.0f, 1.0f);
inputOptions.SetAlphaMode(alphaMode);
var compressionOptions = new CompressionOptions();
compressionOptions.SetFormat(outputFormat);
compressionOptions.SetQuality(Quality.Normal);
// TODO: This isn't working which keeps us from getting the
// same alpha dither behavior on DXT1 as XNA.
//
// See https://github.com/MonoGame/MonoGame/issues/6259
//
//if (alphaDither)
//compressionOptions.SetQuantization(false, false, true);
var outputOptions = new OutputOptions();
outputOptions.SetOutputHeader(false);
outputOptions.SetOutputOptionsOutputHandler(NvttBeginImage, NvttWriteImage, NvttEndImage);
var dxtCompressor = new Compressor();
dxtCompressor.Compress(inputOptions, compressionOptions, outputOptions);
}
finally
{
dataHandle.Free();
}
return true;
}
protected override bool TryCopyTo(BitmapContent destinationBitmap, Rectangle sourceRegion, Rectangle destinationRegion)
{
SurfaceFormat destinationFormat;
if (!destinationBitmap.TryGetFormat(out destinationFormat))
return false;
SurfaceFormat format;
TryGetFormat(out format);
// A shortcut for copying the entire bitmap to another bitmap of the same type and format
var fullRegion = new Rectangle(0, 0, Width, Height);
if ((format == destinationFormat) && (sourceRegion == fullRegion) && (sourceRegion == destinationRegion))
{
destinationBitmap.SetPixelData(GetPixelData());
return true;
}
// No other support for copying from a DXT texture yet
return false;
}
}
}