public override async Task ReadDomainObject(ByteArrayReader reader, DomainHeader header, DomainExportTableEntry export, bool skipProperties, bool skipParse) { await base.ReadDomainObject(reader, header, export, skipProperties, skipParse); if (skipParse) { return; } MipMapsCount = reader.ReadInt32(); for (int i = 0; i < MipMapsCount; ++i) { await ProcessCompressedBulkData(reader, async bulkChunk => { DomainMipMap mip = new DomainMipMap { Width = reader.ReadInt32(), Height = reader.ReadInt32() }; if (mip.Width >= 4 || mip.Height >= 4) { mip.ImageData = (await bulkChunk.DecompressChunk(0))?.GetBytes(); } MipMaps.Add(mip); }); } Guid = await reader.ReadBytes(16); }
BitmapSource GetWPFBitmap(MipMap mip, int maxDimension, bool ShowAlpha) { BitmapSource bmp = null; if (maxDimension != 0) { // Choose a mip of the correct size, if available. var sizedMip = MipMaps.Where(m => (m.Height <= maxDimension && m.Width <= maxDimension) || (m.Width <= maxDimension && m.Height <= maxDimension)); if (sizedMip.Any()) { var mip1 = sizedMip.First(); bmp = mip1.ToImage(); } else { double scale = (double)maxDimension / (Height > Width ? Height : Width); mip = ImageEngine.Resize(mip, scale); bmp = mip.ToImage(); } } else { bmp = mip.ToImage(); } if (!ShowAlpha) { bmp = new FormatConvertedBitmap(bmp, System.Windows.Media.PixelFormats.Bgr32, null, 0); } bmp.Freeze(); return(bmp); }
public override async Task SaveObject(string filename) { if (MipMaps == null || !MipMaps.Any()) { return; } ImageEngineFormat format; DomainMipMap mipMap = MipMaps.Where(mm => mm.ImageData != null && mm.ImageData.Length > 0).OrderByDescending(mm => mm.Width > mm.Height ? mm.Width : mm.Height).FirstOrDefault(); if (mipMap == null) { return; } MemoryStream memory = buildDdsImage(MipMaps.IndexOf(mipMap), out format); if (memory == null) { return; } ImageEngineImage ddsImage = new ImageEngineImage(memory); FileStream stream = new FileStream(filename, FileMode.Create); await Task.Run(() => ddsImage.Save(stream, format, MipHandling.KeepTopOnly)); stream.Close(); memory.Close(); }
public void Write(Stream outputStream) { var mipMaps = MipMaps.Reverse().ToList(); foreach (var mipMap in mipMaps) { mipMap.Write(outputStream); } }
public PNG(BaseImage image) { Width = image.Width; Height = image.Height; foreach (MipMap mipmap in image.MipMaps) { MipMaps.Add(new MipMap(mipmap)); } }
public override Stream GetObjectStream() { if (MipMaps == null || !MipMaps.Any()) { return(null); } ImageEngineFormat format; DomainMipMap mipMap = MipMaps.Where(mm => mm.ImageData != null && mm.ImageData.Length > 0).OrderByDescending(mm => mm.Width > mm.Height ? mm.Width : mm.Height).FirstOrDefault(); return(mipMap == null ? null : buildDdsImage(MipMaps.IndexOf(mipMap), out format)); }
private void PreprocessMipMaps() { bool hwCompressed = Format == TextureContentFormat.DXT1 || Format == TextureContentFormat.DXT3 || Format == TextureContentFormat.DXT5; int width = Width, height = Height; int realCount = 0; for (int i = 0; i < (GenerateMipMaps ? 1 : MipMapCount); i++) { if (hwCompressed) { int dataSize = 0; byte[] data = null; ThreadingHelper.BlockOnUIThread(() => { GL.BindTexture(TextureTarget.Texture2D, texture); GL.GetTexLevelParameter(TextureTarget.Texture2D, i, GetTextureParameter.TextureCompressedImageSize, out dataSize); data = new byte[dataSize]; GL.GetCompressedTexImage(TextureTarget.Texture2D, i, data); }); MipMaps.Add(new TextureContentMipMap(width, height, Format, data)); } else { var bmp = new System.Drawing.Bitmap(width, height); var bmpData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, width, height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); ThreadingHelper.BlockOnUIThread(() => { GL.BindTexture(TextureTarget.Texture2D, texture); GL.GetTexImage(TextureTarget.Texture2D, i, OpenTK.Graphics.OpenGL4.PixelFormat.Bgra, PixelType.UnsignedByte, bmpData.Scan0); }); bmp.UnlockBits(bmpData); MipMaps.Add(new TextureContentMipMap(width, height, Format, bmp)); } width /= 2; height /= 2; realCount++; if (width == 0 || height == 0) { break; } } if (!GenerateMipMaps) { MipMapCount = realCount; } }
/// <summary> /// Creates a WPF Bitmap from largest mipmap. /// Does NOT require that image remains alive. /// </summary> /// <param name="mergeAlpha">Only valid if maxDimension set. True = flattens alpha, directly affecting RGB.</param> /// <param name="maxDimension">Resizes image or uses a mipmap if available.</param> /// <returns>WPF bitmap of largest mipmap.</returns> public BitmapSource GetWPFBitmap(int maxDimension = 0, bool mergeAlpha = false) { MipMap mip = MipMaps[0]; if (maxDimension != 0) { // Choose a mip of the correct size, if available. var sizedMip = MipMaps.Where(m => (m.Height == maxDimension && m.Width <= maxDimension) || (m.Width == maxDimension && m.Height <= maxDimension)); if (sizedMip.Any()) { mip = sizedMip.First(); } else { double scale = maxDimension * 1f / (Height > Width ? Height : Width); mip = ImageEngine.Resize(mip, scale, mergeAlpha); } } mip.BaseImage.Freeze(); return(mip.BaseImage); }
/// <summary> /// Creates a GDI+ bitmap from largest mipmap. /// Does NOT require that image remains alive. /// </summary> /// <param name="ignoreAlpha">True = Previews image without alpha channel.</param> /// <param name="maxDimension">Largest size to display.</param> /// <param name="mergeAlpha">ONLY valid when maxDimension is set. True = flattens alpha, directly affecting RGB.</param> /// <returns>GDI+ bitmap of largest mipmap.</returns> public System.Drawing.Bitmap GetGDIBitmap(bool ignoreAlpha, bool mergeAlpha, int maxDimension = 0) { MipMap mip = MipMaps[0]; if (maxDimension != 0) { // Choose a mip of the correct size, if available. var sizedMip = MipMaps.Where(m => (m.Height == maxDimension && m.Width <= maxDimension) || (m.Width == maxDimension && m.Height <= maxDimension)); if (sizedMip.Any()) { mip = sizedMip.First(); } else { double scale = maxDimension * 1f / (Height > Width ? Height : Width); mip = ImageEngine.Resize(mip, scale, mergeAlpha); } } mip.BaseImage.Freeze(); return(UsefulThings.WinForms.Imaging.CreateBitmap(mip.BaseImage, ignoreAlpha)); }
/// <summary> /// Scales top mipmap and DESTROYS ALL OTHERS. /// </summary> /// <param name="scale">Scaling factor. </param> public void Resize(double scale) { MipMap closestMip = null; double newScale = 0; double desiredSize = MipMaps[0].Width * scale; double min = double.MaxValue; foreach (var mip in MipMaps) { double temp = Math.Abs(mip.Width - desiredSize); if (temp < min) { closestMip = mip; min = temp; } } newScale = desiredSize / closestMip.Width; MipMaps[0] = ImageEngine.Resize(closestMip, newScale); MipMaps.RemoveRange(1, NumMipMaps - 1); }
private void GetFromBitmap(Bitmap bitmap) { Bitmap bmp = new Bitmap(bitmap); Width = bmp.Width; Height = bmp.Height; MipMap mipMap = new MipMap(Width, Height); for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { Color col = bmp.GetPixel(x, y); int index = (y * Width + x) * 4; mipMap.Pixels[index] = col.B; mipMap.Pixels[index + 1] = col.G; mipMap.Pixels[index + 2] = col.R; mipMap.Pixels[index + 3] = col.A; } } MipMaps.Add(mipMap); }
public override async Task SaveObject(string filename, object configuration) { if (MipMaps == null || !MipMaps.Any()) { return; } DdsSaveConfig config = configuration as DdsSaveConfig ?? new DdsSaveConfig(FileFormat.Unknown, 0, 0, false, false); FileFormat format; DomainMipMap mipMap = MipMaps.Where(mm => mm.ImageData != null && mm.ImageData.Length > 0).OrderByDescending(mm => mm.Width > mm.Height ? mm.Width : mm.Height).FirstOrDefault(); if (mipMap == null) { return; } Stream memory = buildDdsImage(MipMaps.IndexOf(mipMap), out format); if (memory == null) { return; } DdsFile ddsImage = new DdsFile(memory); FileStream ddsStream = new FileStream(filename, FileMode.Create); config.FileFormat = format; await Task.Run(() => ddsImage.Save(ddsStream, config)); ddsStream.Close(); memory.Close(); }
protected override void _Read(BinaryReader reader) { Image image = Image.FromStream(reader.BaseStream); Bitmap bmp = new Bitmap(image); Width = bmp.Width; Height = bmp.Height; MipMap mipMap = new MipMap(Width, Height); for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { Color col = bmp.GetPixel(x, y); int index = (y * Width + x) * 4; mipMap.Pixels[index] = col.B; mipMap.Pixels[index + 1] = col.G; mipMap.Pixels[index + 2] = col.R; mipMap.Pixels[index + 3] = col.A; } } MipMaps.Add(mipMap); }
public DDS(D3DFormat format, Bitmap bitmap) { SquishFlags flags = SquishFlags.kDxt1; bool compressed = true; switch (format) { case D3DFormat.DXT1: flags = SquishFlags.kDxt1; break; case D3DFormat.DXT3: flags = SquishFlags.kDxt3; break; case D3DFormat.DXT5: flags = SquishFlags.kDxt5; break; default: compressed = false; break; } Format = format; Width = bitmap.Width; Height = bitmap.Height; MipMap mip = new MipMap { Width = Width, Height = Height }; byte[] data = new byte[mip.Width * mip.Height * 4]; BitmapData bmpdata = bitmap.LockBits(new Rectangle(0, 0, mip.Width, mip.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); Marshal.Copy(bmpdata.Scan0, data, 0, bmpdata.Stride * bmpdata.Height); bitmap.UnlockBits(bmpdata); if (compressed) { for (uint i = 0; i < data.Length - 4; i += 4) { byte r = data[i + 0]; data[i + 0] = data[i + 2]; data[i + 2] = r; } byte[] dest = new byte[Squish.Squish.GetStorageRequirements(mip.Width, mip.Height, flags | SquishFlags.kColourIterativeClusterFit)]; Squish.Squish.CompressImage(data, mip.Width, mip.Height, ref dest, flags | SquishFlags.kColourIterativeClusterFit); mip.Data = dest; } else { mip.Data = data; } MipMaps.Add(mip); }
/// <summary> /// Scales top mipmap and DESTROYS ALL OTHERS. /// </summary> /// <param name="scale">Scaling factor. </param> /// <param name="mergeAlpha">True = flattens alpha, directly affecting RGB.</param> public void Resize(double scale, bool mergeAlpha) { MipMaps[0] = ImageEngine.Resize(MipMaps[0], scale, mergeAlpha); MipMaps.RemoveRange(1, NumMipMaps - 1); }
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); }
public override async Task SetObject(string filename, List <DomainNameTableEntry> nameTable, object configuration) { DdsSaveConfig config = configuration as DdsSaveConfig ?? new DdsSaveConfig(FileFormat.Unknown, 0, 0, false, false); DdsFile image = await Task.Run(() => new DdsFile(filename)); bool skipFirstMip = false; int width = image.Width; int height = image.Height; if (MipMaps[0].ImageData == null || MipMaps[0].ImageData.Length == 0) { width *= 2; height *= 2; skipFirstMip = true; } DomainPropertyIntValue sizeX = PropertyHeader.GetProperty("SizeX").FirstOrDefault()?.Value as DomainPropertyIntValue; DomainPropertyIntValue sizeY = PropertyHeader.GetProperty("SizeY").FirstOrDefault()?.Value as DomainPropertyIntValue; sizeX?.SetPropertyValue(skipFirstMip ? width * 2 : width); sizeY?.SetPropertyValue(skipFirstMip ? height * 2 : height); DomainPropertyIntValue mipTailBaseIdx = PropertyHeader.GetProperty("MipTailBaseIdx").FirstOrDefault()?.Value as DomainPropertyIntValue; int indexSize = width > height ? width : height; mipTailBaseIdx?.SetPropertyValue((int)Math.Log(skipFirstMip ? indexSize * 2 : indexSize, 2)); DomainPropertyStringValue filePath = PropertyHeader.GetProperty("SourceFilePath").FirstOrDefault()?.Value as DomainPropertyStringValue; DomainPropertyStringValue fileTime = PropertyHeader.GetProperty("SourceFileTimestamp").FirstOrDefault()?.Value as DomainPropertyStringValue; filePath?.SetPropertyValue(filename); fileTime?.SetPropertyValue(File.GetLastWriteTime(filename).ToString("yyyy-MM-dd hh:mm:ss")); DomainPropertyByteValue pfFormat = PropertyHeader.GetProperty("Format").FirstOrDefault()?.Value as DomainPropertyByteValue; FileFormat imageFormat = FileFormat.Unknown; if (pfFormat != null) { imageFormat = DdsPixelFormat.ParseFileFormat(pfFormat.PropertyString); } if (imageFormat == FileFormat.Unknown) { throw new Exception($"Unknown DDS File Format ({pfFormat?.PropertyString ?? "Unknown"})."); } config.FileFormat = imageFormat; MipMaps.Clear(); if (skipFirstMip) { MipMaps.Add(new DomainMipMap { ImageData = null, Width = width, Height = height }); } image.GenerateMipMaps(4, 4); foreach (DdsMipMap mipMap in image.MipMaps.OrderByDescending(mip => mip.Width)) { MipMaps.Add(new DomainMipMap { ImageData = image.WriteMipMap(mipMap, config), Width = mipMap.Width, Height = mipMap.Height }); } }
public override async Task SetObject(string filename, List <DomainNameTableEntry> nameTable) { ImageEngineImage image = await Task.Run(() => new ImageEngineImage(filename)); int width = image.Width; int height = image.Height; DomainPropertyIntValue sizeX = PropertyHeader.GetProperty("SizeX").FirstOrDefault()?.Value as DomainPropertyIntValue; DomainPropertyIntValue sizeY = PropertyHeader.GetProperty("SizeY").FirstOrDefault()?.Value as DomainPropertyIntValue; sizeX?.SetPropertyValue(width); sizeY?.SetPropertyValue(height); DomainPropertyIntValue mipTailBaseIdx = PropertyHeader.GetProperty("MipTailBaseIdx").FirstOrDefault()?.Value as DomainPropertyIntValue; mipTailBaseIdx?.SetPropertyValue((int)Math.Log(width > height ? width : height, 2)); DomainPropertyStringValue filePath = PropertyHeader.GetProperty("SourceFilePath").FirstOrDefault()?.Value as DomainPropertyStringValue; DomainPropertyStringValue fileTime = PropertyHeader.GetProperty("SourceFileTimestamp").FirstOrDefault()?.Value as DomainPropertyStringValue; filePath?.SetPropertyValue(filename); fileTime?.SetPropertyValue(File.GetLastWriteTime(filename).ToString("yyyy-MM-dd hh:mm:ss")); DomainPropertyByteValue pfFormat = PropertyHeader.GetProperty("Format").FirstOrDefault()?.Value as DomainPropertyByteValue; ImageEngineFormat imageFormat = image.Format.InternalFormat; if (!imageFormat.ToString().Contains("DDS")) { throw new Exception($"Image is not in a DDS format. It is actually {imageFormat}."); } if (pfFormat != null) { string formatStr = imageFormat.ToString().Replace("DDS", "PF"); if (formatStr.Contains("ARGB")) { formatStr = "PF_A8R8G8B8"; } else if (formatStr.Contains("G8")) { formatStr = "PF_G8"; } DomainNameTableEntry formatTableEntry = nameTable.SingleOrDefault(nt => nt.Name.String == formatStr) ?? nameTable.AddDomainNameTableEntry(formatStr); pfFormat.SetPropertyValue(formatTableEntry); } MipMaps.Clear(); while (true) { MemoryStream stream = new MemoryStream(); image.Save(stream, imageFormat, MipHandling.KeepTopOnly); await stream.FlushAsync(); MipMaps.Add(new DomainMipMap { ImageData = (await ByteArrayReader.CreateNew(stream.ToArray(), 0x80).Splice()).GetBytes(), // Strip off 128 bytes for the DDS header Width = image.Width, Height = image.Height }); if (width == 1 && height == 1) { break; } if (width > 1) { width /= 2; } if (height > 1) { height /= 2; } if (image.Width > 4 && image.Height > 4) { image.Resize(0.5, false); } } }