internal string GetAutoSavePath(ImageEngineFormat newformat) { string newpath = null; bool acceptablePath = false; int count = 1; string formatString = ImageFormats.GetExtensionOfFormat(newformat); string basepath = Path.GetDirectoryName(ImagePath) + "\\" + Path.GetFileNameWithoutExtension(ImagePath) + "." + (newformat == ImageEngineFormat.Unknown ? Path.GetExtension(ImagePath) : formatString); newpath = basepath; // KFreon: Check that path is not already taken while (!acceptablePath) { if (File.Exists(newpath)) { newpath = Path.Combine(Path.GetDirectoryName(basepath), Path.GetFileNameWithoutExtension(basepath) + "_" + count++ + Path.GetExtension(basepath)); } else { acceptablePath = true; } } return(newpath); }
/// <summary> /// Creates a Mipmap object from a WPF image. /// </summary> public MipMap(byte[] pixels, int width, int height, ImageFormats.ImageEngineFormatDetails details) { Pixels = pixels; Width = width; Height = height; LoadedFormatDetails = details; UncompressedSize = ImageFormats.GetUncompressedSize(width, height, details.MaxNumberOfChannels, false); }
internal static AbstractHeader LoadHeader(Stream stream) { stream.Seek(0, SeekOrigin.Begin); // Determine type of image ImageFormats.SupportedExtensions ext = ImageFormats.DetermineImageType(stream); // Parse header AbstractHeader header = null; switch (ext) { case ImageFormats.SupportedExtensions.BMP: header = new BMP_Header(stream); break; case ImageFormats.SupportedExtensions.DDS: header = new DDS_Header(stream); break; case ImageFormats.SupportedExtensions.JPG: header = new JPG_Header(stream); break; case ImageFormats.SupportedExtensions.PNG: header = new PNG_Header(stream); break; case ImageFormats.SupportedExtensions.TGA: header = new TGA_Header(stream); break; case ImageFormats.SupportedExtensions.GIF: header = new GIF_Header(stream); break; case ImageFormats.SupportedExtensions.TIF: header = new TIFF_Header(stream); break; default: throw new NotSupportedException("Image type unknown."); } return(header); }
/// <summary> /// Loads image from stream. /// </summary> /// <param name="stream">Full image stream.</param> /// <param name="Format">Detected Format.</param> /// <param name="extension">File Extension. Used to determine format more easily.</param> /// <param name="maxWidth">Maximum width to allow when loading. Resized if enforceResize = true.</param> /// <param name="maxHeight">Maximum height to allow when loading. Resized if enforceResize = true.</param> /// <param name="enforceResize">True = Resizes image to match either maxWidth or maxHeight.</param> /// <param name="header">DDS header of image.</param> /// <param name="mergeAlpha">ONLY valid when enforceResize is true. True = Flattens alpha down, directly affecting RGB.</param> /// <returns>List of Mipmaps.</returns> internal static List <MipMap> LoadImage(Stream stream, out Format Format, string extension, int maxWidth, int maxHeight, bool enforceResize, out DDSGeneral.DDS_HEADER header, bool mergeAlpha) { // KFreon: See if image is built-in codec agnostic. header = null; Format = ImageFormats.ParseFormat(stream, extension, ref header); List <MipMap> MipMaps = null; switch (Format.SurfaceFormat) { case ImageEngineFormat.BMP: case ImageEngineFormat.JPG: case ImageEngineFormat.PNG: MipMaps = WIC_Codecs.LoadWithCodecs(stream, maxWidth, maxHeight, false); break; case ImageEngineFormat.DDS_DXT1: case ImageEngineFormat.DDS_DXT2: case ImageEngineFormat.DDS_DXT3: case ImageEngineFormat.DDS_DXT4: case ImageEngineFormat.DDS_DXT5: if (WindowsWICCodecsAvailable) { MipMaps = WIC_Codecs.LoadWithCodecs(stream, maxWidth, maxHeight, true); } else { MipMaps = DDSGeneral.LoadDDS(stream, header, Format, maxHeight > maxWidth ? maxHeight : maxWidth); } break; case ImageEngineFormat.DDS_ARGB: case ImageEngineFormat.DDS_A8L8: case ImageEngineFormat.DDS_RGB: case ImageEngineFormat.DDS_ATI1: case ImageEngineFormat.DDS_ATI2_3Dc: case ImageEngineFormat.DDS_G8_L8: case ImageEngineFormat.DDS_V8U8: MipMaps = DDSGeneral.LoadDDS(stream, header, Format, maxHeight > maxWidth ? maxHeight : maxWidth); break; case ImageEngineFormat.TGA: var img = new TargaImage(stream); byte[] pixels = UsefulThings.WinForms.Imaging.GetPixelDataFromBitmap(img.Image); WriteableBitmap wbmp = UsefulThings.WPF.Images.CreateWriteableBitmap(pixels, img.Image.Width, img.Image.Height); var mip1 = new MipMap(wbmp); MipMaps = new List <MipMap>() { mip1 }; img.Dispose(); break; default: throw new InvalidDataException("Image format is unknown."); } if (MipMaps == null || MipMaps.Count == 0) { throw new InvalidDataException("No mipmaps loaded."); } // KFreon: No resizing requested if (maxHeight == 0 && maxWidth == 0) { return(MipMaps); } // KFreon: Test if we need to resize var top = MipMaps.First(); if (top.Width == maxWidth || top.Height == maxHeight) { return(MipMaps); } int max = maxWidth > maxHeight ? maxWidth : maxHeight; // KFreon: Attempt to resize var sizedMips = MipMaps.Where(m => m.Width > m.Height ? m.Width <= max : m.Height <= max); if (sizedMips != null && sizedMips.Any()) // KFreon: If there's already a mip, return that. { MipMaps = sizedMips.ToList(); } else if (enforceResize) { // Get top mip and clear others. var mip = MipMaps[0]; MipMaps.Clear(); MipMap output = null; int divisor = mip.Width > mip.Height ? mip.Width / max : mip.Height / max; output = Resize(mip, 1f / divisor, mergeAlpha); MipMaps.Add(output); } return(MipMaps); }
byte[] AttemptSaveUsingOriginalData(ImageFormats.ImageEngineFormatDetails destFormatDetails, MipHandling GenerateMips, int desiredMaxDimension, int mipToSave, AlphaSettings alphaSetting) { int start = 0; int destStart = 0; int length = OriginalData.Length; int newWidth = Width; int newHeight = Height; DDS_Header tempHeader = null; byte[] data = null; byte[] tempOriginalData = OriginalData; if (destFormatDetails.IsDDS) { destStart = destFormatDetails.HeaderSize; start = destStart; int mipCount = 0; if (mipToSave != 0) { mipCount = 1; newWidth = MipMaps[mipToSave].Width; newHeight = MipMaps[mipToSave].Height; start = ImageFormats.GetCompressedSize(mipToSave, destFormatDetails, Width, Height); length = ImageFormats.GetCompressedSize(1, destFormatDetails, newWidth, newHeight); } else if (desiredMaxDimension != 0 && desiredMaxDimension < Width && desiredMaxDimension < Height) { int index = MipMaps.FindIndex(t => t.Width < desiredMaxDimension && t.Height < desiredMaxDimension); // If none found, do a proper save and see what happens. if (index == -1) { return(ImageEngine.Save(MipMaps, destFormatDetails, GenerateMips, alphaSetting, desiredMaxDimension, mipToSave)); } mipCount -= index; newWidth = MipMaps[index].Width; newHeight = MipMaps[index].Height; start = ImageFormats.GetCompressedSize(index, destFormatDetails, Width, Height); length = ImageFormats.GetCompressedSize(mipCount, destFormatDetails, newWidth, newHeight); } else { if (alphaSetting == AlphaSettings.RemoveAlphaChannel) { // Can't edit alpha directly in premultiplied formats. Not easily anyway. if (destFormatDetails.IsPremultipliedFormat) { return(ImageEngine.Save(MipMaps, destFormatDetails, GenerateMips, alphaSetting, desiredMaxDimension, mipToSave)); } // DDS Formats only switch (destFormatDetails.Format) { // Excluded cos they have no true alpha case ImageEngineFormat.DDS_A8: case ImageEngineFormat.DDS_A8L8: case ImageEngineFormat.DDS_ATI1: case ImageEngineFormat.DDS_ATI2_3Dc: case ImageEngineFormat.DDS_V8U8: case ImageEngineFormat.DDS_G16_R16: case ImageEngineFormat.DDS_G8_L8: case ImageEngineFormat.DDS_R5G6B5: case ImageEngineFormat.DDS_RGB_8: case ImageEngineFormat.DDS_DXT1: break; // Exluded cos they're alpha isn't easily edited case ImageEngineFormat.DDS_DXT2: case ImageEngineFormat.DDS_DXT4: break; // Excluded cos they're currently unsupported case ImageEngineFormat.DDS_CUSTOM: case ImageEngineFormat.DDS_DX10: case ImageEngineFormat.DDS_ARGB_4: break; case ImageEngineFormat.DDS_ABGR_8: case ImageEngineFormat.DDS_ARGB_32F: case ImageEngineFormat.DDS_ARGB_8: case ImageEngineFormat.DDS_DXT3: case ImageEngineFormat.DDS_DXT5: tempOriginalData = new byte[OriginalData.Length]; Array.Copy(OriginalData, tempOriginalData, OriginalData.Length); // Edit alpha values int alphaStart = 128; int alphaJump = 0; byte[] alphaBlock = null; if (destFormatDetails.IsBlockCompressed) { alphaJump = 16; alphaBlock = new byte[8]; for (int i = 0; i < 8; i++) { alphaBlock[i] = 255; } } else { alphaJump = destFormatDetails.ComponentSize * 4; alphaBlock = new byte[destFormatDetails.ComponentSize]; switch (destFormatDetails.ComponentSize) { case 1: alphaBlock[0] = 255; break; case 2: alphaBlock = BitConverter.GetBytes(ushort.MaxValue); break; case 4: alphaBlock = BitConverter.GetBytes(1f); break; } } for (int i = alphaStart; i < OriginalData.Length; i += alphaJump) { Array.Copy(alphaBlock, 0, tempOriginalData, i, alphaBlock.Length); } break; } } switch (GenerateMips) { case MipHandling.KeepExisting: mipCount = NumMipMaps; break; case MipHandling.Default: if (NumMipMaps > 1) { mipCount = NumMipMaps; } else { goto case MipHandling.GenerateNew; // Eww goto... } break; case MipHandling.GenerateNew: ImageEngine.DestroyMipMaps(MipMaps); ImageEngine.TestDDSMipSize(MipMaps, destFormatDetails, Width, Height, out double fixXScale, out double fixYScale, GenerateMips); // Wrong sizing, so can't use original data anyway. if (fixXScale != 0 || fixYScale != 0) { return(ImageEngine.Save(MipMaps, destFormatDetails, GenerateMips, alphaSetting, desiredMaxDimension, mipToSave)); } mipCount = DDSGeneral.BuildMipMaps(MipMaps); // Compress mipmaps excl top byte[] formattedMips = DDSGeneral.Save(MipMaps.GetRange(1, MipMaps.Count - 1), destFormatDetails, alphaSetting); if (formattedMips == null) { return(null); } // Get top mip size and create destination array length = ImageFormats.GetCompressedSize(0, destFormatDetails, newWidth, newHeight); // Should be the length of the top mipmap. data = new byte[formattedMips.Length + length]; // Copy smaller mips to destination Array.Copy(formattedMips, destFormatDetails.HeaderSize, data, length, formattedMips.Length - destFormatDetails.HeaderSize); break; case MipHandling.KeepTopOnly: mipCount = 1; length = ImageFormats.GetCompressedSize(1, destFormatDetails, newWidth, newHeight); break; } } // Header tempHeader = new DDS_Header(mipCount, newHeight, newWidth, destFormatDetails.Format, destFormatDetails.DX10Format); } // Use existing array, otherwise create one. data = data ?? new byte[length]; Array.Copy(tempOriginalData, start, data, destStart, length - destStart); // Write header if existing (DDS Only) if (tempHeader != null) { tempHeader.WriteToArray(data, 0); } return(data); }
/// <summary> /// Reads DDS format from DDS Header. /// Not guaranteed to work. Format 'optional' in header. /// </summary> /// <param name="stream">Stream containing full image file. NOT just pixels.</param> /// <param name="header">DDS Header information.</param> /// <returns>Format of DDS.</returns> internal static Format ParseDDSFormat(Stream stream, out DDS_HEADER header) { Format format = new Format(ImageEngineFormat.DDS_ARGB); stream.Seek(0, SeekOrigin.Begin); using (BinaryReader reader = new BinaryReader(stream, Encoding.Default, true)) { header = null; // KFreon: Check image is a DDS int Magic = reader.ReadInt32(); if (Magic != 0x20534444) { return(new Format()); // KFreon: Not a DDS } header = new DDS_HEADER(); Read_DDS_HEADER(header, reader); if (((header.ddspf.dwFlags & 0x00000004) != 0) && (header.ddspf.dwFourCC == 0x30315844 /*DX10*/)) { throw new Exception("DX10 not supported yet!"); } format = ImageFormats.ParseFourCC(header.ddspf.dwFourCC); if (format.SurfaceFormat == ImageEngineFormat.Unknown || format.SurfaceFormat == ImageEngineFormat.DDS_ARGB) { // KFreon: Apparently all these flags mean it's a V8U8 image... if (header.ddspf.dwRGBBitCount == 0x10 && header.ddspf.dwRBitMask == 0xFF && header.ddspf.dwGBitMask == 0xFF00 && header.ddspf.dwBBitMask == 0x00 && header.ddspf.dwABitMask == 0x00) { format = new Format(ImageEngineFormat.DDS_V8U8); // KFreon: V8U8 } // KFreon: Test for L8/G8 else if (header.ddspf.dwABitMask == 0 && header.ddspf.dwBBitMask == 0 && header.ddspf.dwGBitMask == 0 && header.ddspf.dwRBitMask == 255 && header.ddspf.dwFlags == 131072 && header.ddspf.dwSize == 32 && header.ddspf.dwRGBBitCount == 8) { format = new Format(ImageEngineFormat.DDS_G8_L8); } // KFreon: A8L8. This can probably be something else as well, but it seems to work for now else if (header.ddspf.dwRGBBitCount == 16) { format = new Format(ImageEngineFormat.DDS_A8L8); } // KFreon: RGB test. else if (header.ddspf.dwRGBBitCount == 24) { format = new Format(ImageEngineFormat.DDS_RGB); } } } return(format); }