internal static byte[] Save(List<MipMap> mipMaps, ImageEngineFormat saveFormat, AlphaSettings alphaSetting, List<uint> customMasks = null)
{
// Set compressor for Block Compressed textures
Action<byte[], int, int, byte[], int, AlphaSettings> compressor = null;
bool needCheckSize = saveFormat.ToString().Contains("DXT") || saveFormat.ToString().Contains("ATI");
switch (saveFormat)
{
case ImageEngineFormat.DDS_ATI1:
compressor = DDS_Encoders.CompressBC4Block;
break;
case ImageEngineFormat.DDS_ATI2_3Dc:
compressor = DDS_Encoders.CompressBC5Block;
break;
case ImageEngineFormat.DDS_DX10:
Debugger.Break();
break; // TODO: NOT SUPPORTED YET. DX10
case ImageEngineFormat.DDS_DXT1:
compressor = DDS_Encoders.CompressBC1Block;
break;
case ImageEngineFormat.DDS_DXT2:
case ImageEngineFormat.DDS_DXT3:
compressor = DDS_Encoders.CompressBC2Block;
break;
case ImageEngineFormat.DDS_DXT4:
case ImageEngineFormat.DDS_DXT5:
compressor = DDS_Encoders.CompressBC3Block;
break;
}
int height = mipMaps[0].Height;
int width = mipMaps[0].Width;
if (needCheckSize && !CheckSize_DXT(width, height))
throw new InvalidOperationException($"DXT compression formats require dimensions to be multiples of 4. Got: {width}x{height}.");
int fullSize = GetCompressedSizeOfImage(mipMaps.Count, saveFormat, width, height);
// +1 to get the full size, not just the offset of the last mip.
//int fullSize = GetMipOffset(mipMaps.Count + 1, saveFormat, mipMaps[0].Width, mipMaps[0].Height);
byte[] destination = new byte[fullSize];
// Create header and write to destination
DDS_Header header = new DDS_Header(mipMaps.Count, height, width, saveFormat, customMasks);
header.WriteToArray(destination, 0);
int blockSize = ImageFormats.GetBlockSize(saveFormat);
if (ImageFormats.IsBlockCompressed(saveFormat))
{
int mipOffset = 128;
foreach (MipMap mipmap in mipMaps)
mipOffset = WriteCompressedMipMap(destination, mipOffset, mipmap, blockSize, compressor, alphaSetting);
}
else
{
// UNCOMPRESSED
var action = new Action<int>(mipIndex =>
{
if (alphaSetting == AlphaSettings.RemoveAlphaChannel)
{
// Remove alpha by setting AMask = 0
var ddspf = header.ddspf;
ddspf.dwABitMask = 0;
header.ddspf = ddspf;
}
// Get MipOffset
int offset = GetMipOffset(mipIndex, saveFormat, width, height);
WriteUncompressedMipMap(destination, offset, mipMaps[mipIndex], saveFormat, header.ddspf);
});
if (ImageEngine.EnableThreading)
Parallel.For(0, mipMaps.Count, new ParallelOptions { MaxDegreeOfParallelism = ImageEngine.NumThreads }, action);
else
for (int i = 0; i < mipMaps.Count; i++)
action(i);
}
return destination;
}