internal static List<MipMap> LoadDDS(MemoryStream compressed, DDS_Header header, int desiredMaxDimension)
{
MipMap[] MipMaps = null;
int mipWidth = header.Width;
int mipHeight = header.Height;
ImageEngineFormat format = header.Format;
int blockSize = ImageFormats.GetBlockSize(format);
int estimatedMips = header.dwMipMapCount;
int mipOffset = 128; // Includes header.
// TODO: Incorrect mip offset for DX10
if (!EnsureMipInImage(compressed.Length, mipWidth, mipHeight, 4, format, out mipOffset)) // Update number of mips too
estimatedMips = 1;
if (estimatedMips == 0)
estimatedMips = EstimateNumMipMaps(mipWidth, mipHeight);
mipOffset = 128; // Needs resetting after checking there's mips in this image.
// TESTUNIG
if (estimatedMips == 0)
estimatedMips = 1;
int orig_estimatedMips = estimatedMips; // Store original count for later (uncompressed only I think)
// KFreon: Decide which mip to start loading at - going to just load a few mipmaps if asked instead of loading all, then choosing later. That's slow.
if (desiredMaxDimension != 0 && estimatedMips > 1)
{
if (!EnsureMipInImage(compressed.Length, mipWidth, mipHeight, desiredMaxDimension, format, out mipOffset)) // Update number of mips too
throw new InvalidDataException($"Requested mipmap does not exist in this image. Top Image Size: {mipWidth}x{mipHeight}, requested mip max dimension: {desiredMaxDimension}.");
// Not the first mipmap.
if (mipOffset > 128)
{
double divisor = mipHeight > mipWidth ? mipHeight / desiredMaxDimension : mipWidth / desiredMaxDimension;
mipHeight = (int)(mipHeight / divisor);
mipWidth = (int)(mipWidth / divisor);
if (mipWidth == 0 || mipHeight == 0) // Reset as a dimension is too small to resize
{
mipHeight = header.Height;
mipWidth = header.Width;
mipOffset = 128;
}
else
{
// Update estimated mips due to changing dimensions.
estimatedMips = EstimateNumMipMaps(mipWidth, mipHeight);
}
}
else // The first mipmap
mipOffset = 128;
}
// Move to requested mipmap
compressed.Position = mipOffset;
// Block Compressed texture chooser.
Action<byte[], int, byte[], int, int, bool> DecompressBCBlock = null;
switch (format)
{
case ImageEngineFormat.DDS_DXT1:
DecompressBCBlock = DDS_Decoders.DecompressBC1Block;
break;
case ImageEngineFormat.DDS_DXT2:
case ImageEngineFormat.DDS_DXT3:
DecompressBCBlock = DDS_Decoders.DecompressBC2Block;
break;
case ImageEngineFormat.DDS_DXT4:
case ImageEngineFormat.DDS_DXT5:
DecompressBCBlock = DDS_Decoders.DecompressBC3Block;
break;
case ImageEngineFormat.DDS_ATI1:
DecompressBCBlock = DDS_Decoders.DecompressATI1;
break;
case ImageEngineFormat.DDS_ATI2_3Dc:
DecompressBCBlock = DDS_Decoders.DecompressATI2Block;
break;
}
MipMaps = new MipMap[estimatedMips];
// KFreon: Read mipmaps
if (ImageFormats.IsBlockCompressed(format)) // Threading done in the decompression, not here.
{
for (int m = 0; m < estimatedMips; m++)
{
// KFreon: If mip is too small, skip out. This happens most often with non-square textures. I think it's because the last mipmap is square instead of the same aspect.
if (mipWidth <= 0 || mipHeight <= 0) // Needed cos it doesn't throw when reading past the end for some reason.
{
break;
}
MipMap mipmap = ReadCompressedMipMap(compressed, mipWidth, mipHeight, blockSize, mipOffset, (format == ImageEngineFormat.DDS_DXT2 || format == ImageEngineFormat.DDS_DXT4), DecompressBCBlock);
MipMaps[m] = mipmap;
mipOffset += (int)(mipWidth * mipHeight * (blockSize / 16d)); // Also put the division in brackets cos if the mip dimensions are high enough, the multiplications can exceed int.MaxValue)
mipWidth /= 2;
mipHeight /= 2;
}
}
else
{
int startMip = orig_estimatedMips - estimatedMips;
// UNCOMPRESSED - Can't really do threading in "decompression" so do it for the mipmaps.
var action = new Action<int>(mipIndex =>
{
// Calculate mipOffset and dimensions
int offset, width, height;
offset = GetMipOffset(mipIndex, format, header.Width, header.Height);
double divisor = mipIndex == 0 ? 1d : 2 << (mipIndex - 1); // Divisor represents 2^mipIndex - Math.Pow seems very slow.
width = (int)(header.Width / divisor);
height = (int)(header.Height / divisor);
MipMap mipmap = null;
try
{
mipmap = ReadUncompressedMipMap(compressed, offset, width, height, header.ddspf);
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
MipMaps[mipIndex] = mipmap;
});
if (ImageEngine.EnableThreading)
Parallel.For(startMip, orig_estimatedMips, new ParallelOptions { MaxDegreeOfParallelism = ImageEngine.NumThreads }, action);
else
for (int i = startMip; i < orig_estimatedMips; i++)
action(i);
}
List<MipMap> mips = new List<MipMap>(MipMaps.Where(t => t != null));
if (mips.Count == 0)
throw new InvalidOperationException($"No mipmaps loaded. Estimated mips: {estimatedMips}, mip dimensions: {mipWidth}x{mipHeight}");
return mips;
}