(ded4a3e0a) v0.9.0.7
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user