/// <summary>
/// Save mipmaps as given format to stream.
/// </summary>
/// <param name="MipMaps">List of Mips to save.</param>
/// <param name="format">Desired format.</param>
/// <param name="destination">Stream to save to.</param>
/// <param name="mipChoice">Determines how to handle mipmaps.</param>
/// <param name="maxDimension">Maximum value for either image dimension.</param>
/// <param name="mergeAlpha">True = alpha flattened down, directly affecting RGB.</param>
/// <param name="mipToSave">0 based index on which mipmap to make top of saved image.</param>
/// <returns>True on success.</returns>
internal static bool Save(List <MipMap> MipMaps, ImageEngineFormat format, Stream destination, MipHandling mipChoice, bool mergeAlpha, int maxDimension = 0, int mipToSave = 0)
{
Format temp = new Format(format);
List <MipMap> newMips = new List <MipMap>(MipMaps);
if ((temp.IsMippable && mipChoice == MipHandling.GenerateNew) || (temp.IsMippable && newMips.Count == 1 && mipChoice == MipHandling.Default))
{
DDSGeneral.BuildMipMaps(newMips, mergeAlpha);
}
// KFreon: Resize if asked
if (maxDimension != 0 && maxDimension < newMips[0].Width && maxDimension < newMips[0].Height)
{
if (!UsefulThings.General.IsPowerOfTwo(maxDimension))
{
throw new ArgumentException($"{nameof(maxDimension)} must be a power of 2. Got {nameof(maxDimension)} = {maxDimension}");
}
// KFreon: Check if there's a mipmap suitable, removes all larger mipmaps
var validMipmap = newMips.Where(img => (img.Width == maxDimension && img.Height <= maxDimension) || (img.Height == maxDimension && img.Width <= maxDimension)); // Check if a mip dimension is maxDimension and that the other dimension is equal or smaller
if (validMipmap?.Count() != 0)
{
int index = newMips.IndexOf(validMipmap.First());
newMips.RemoveRange(0, index);
}
else
{
// KFreon: Get the amount the image needs to be scaled. Find largest dimension and get it's scale.
double scale = maxDimension * 1f / (newMips[0].Width > newMips[0].Height ? newMips[0].Width: newMips[0].Height);
// KFreon: No mip. Resize.
newMips[0] = Resize(newMips[0], scale, mergeAlpha);
}
}
// KFreon: Ensure we have a power of two for dimensions
double fixScale = 0;
if (!UsefulThings.General.IsPowerOfTwo(newMips[0].Width) || !UsefulThings.General.IsPowerOfTwo(newMips[0].Height))
{
int newWidth = UsefulThings.General.RoundToNearestPowerOfTwo(newMips[0].Width);
int newHeigh = UsefulThings.General.RoundToNearestPowerOfTwo(newMips[0].Height);
// KFreon: Assuming same scale in both dimensions...
fixScale = 1.0 * newWidth / newMips[0].Width;
newMips[0] = Resize(newMips[0], fixScale, mergeAlpha);
}
if (fixScale != 0 || mipChoice == MipHandling.KeepTopOnly)
{
DestroyMipMaps(newMips, mipToSave);
}
if (fixScale != 0 && temp.IsMippable && mipChoice != MipHandling.KeepTopOnly)
{
DDSGeneral.BuildMipMaps(newMips, mergeAlpha);
}
bool result = false;
if (temp.SurfaceFormat.ToString().Contains("DDS"))
{
result = DDSGeneral.Save(newMips, destination, temp);
}
else
{
// KFreon: Try saving with built in codecs
var mip = newMips[0];
if (WindowsWICCodecsAvailable)
{
result = WIC_Codecs.SaveWithCodecs(mip.BaseImage, destination, format);
}
}
if (mipChoice != MipHandling.KeepTopOnly && temp.IsMippable)
{
// KFreon: Necessary. Must be how I handle the lowest mip levels. i.e. WRONGLY :(
// Figure out how big the file should be and make it that size
int size = 0;
int width = newMips[0].Width;
int height = newMips[0].Height;
int divisor = 1;
if (temp.IsBlockCompressed)
{
divisor = 4;
}
while (width >= 1 && height >= 1)
{
int tempWidth = width;
int tempHeight = height;
if (temp.IsBlockCompressed)
{
if (tempWidth < 4)
{
tempWidth = 4;
}
if (tempHeight < 4)
{
tempHeight = 4;
}
}
size += tempWidth / divisor * tempHeight / divisor * temp.BlockSize;
width /= 2;
height /= 2;
}
if (size > destination.Length - 128)
{
byte[] blanks = new byte[size - (destination.Length - 128)];
destination.Write(blanks, 0, blanks.Length);
}
}
return(result);
}