public void ExtractBitmap(Bitmap bitmap, int imageIndex, Stream outStream) { var resource = bitmap.Resources[imageIndex]; var resourceContext = new ResourceSerializationContext(CacheContext, resource.Resource); var definition = ExtractResourceDefinition(resource, resourceContext); var header = new DDSHeader(definition.Texture.Definition); header.Write(new EndianWriter(outStream)); ExtractResourceData(definition, resource, outStream); }
private void ExtractBitmap(CacheFile.IndexItem blamTag, string directory) { Console.WriteLine($"{blamTag.Name}"); // // Load the Blam tag definition // var blamContext = new CacheSerializationContext(ref BlamCache, blamTag); var bitmap = BlamCache.Deserializer.Deserialize <Bitmap>(blamContext); var ddsOutDir = directory; string bitmap_name = blamTag.Name.Replace('\\', '_'); if (bitmap.Images.Count > 1) { ddsOutDir = Path.Combine(directory, bitmap_name); Directory.CreateDirectory(ddsOutDir); } for (var i = 0; i < bitmap.Images.Count; i++) { var outPath = Path.Combine(ddsOutDir, ((bitmap.Images.Count > 1) ? i.ToString() : bitmap_name) + ".dds"); var image = bitmap.Images[i]; // // Get bitmap data and write file // BaseBitmap baseBitmap = ExtractBitmapData(bitmap, i); // Bitmap is not valid (not a failure to convert, tag data is not valid / no data to convert if (baseBitmap == null) { return; } var header = new DDSHeader(baseBitmap); using (var outStream = File.Open(outPath, FileMode.Create, FileAccess.Write)) { header.Write(new EndianWriter(outStream)); var dataStream = new MemoryStream(baseBitmap.Data); StreamUtil.Copy(dataStream, outStream, baseBitmap.Data.Length); } } }
private static void GenerateCompressedMipMaps(BaseBitmap bitmap) { string tempBitmap = $@"Temp\{Guid.NewGuid().ToString()}.dds"; if (!Directory.Exists(@"Temp")) { Directory.CreateDirectory(@"Temp"); } //Write input dds bitmap.MipMapCount = 0; var header = new DDSHeader(bitmap); using (var outStream = File.Open(tempBitmap, FileMode.Create, FileAccess.Write)) { header.Write(new EndianWriter(outStream)); var dataStream = new MemoryStream(bitmap.Data); StreamUtil.Copy(dataStream, outStream, bitmap.Data.Length); } string args = " "; switch (bitmap.Format) { case BitmapFormat.Dxn: args += "-bc5 -resize -normal"; break; case BitmapFormat.Dxt1: args += "-bc1 "; break; case BitmapFormat.Dxt3: args += "-bc2 "; break; case BitmapFormat.Dxt5: args += "-bc3 "; break; default: bitmap.MipMapCount = 0; if (File.Exists(tempBitmap)) { File.Delete(tempBitmap); } return; } args += $"{tempBitmap} {tempBitmap}"; ProcessStartInfo info = new ProcessStartInfo(@"Tools\nvcompress.exe") { Arguments = args, CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden, UseShellExecute = false, RedirectStandardError = false, RedirectStandardOutput = false, RedirectStandardInput = false }; Process nvcompress = Process.Start(info); nvcompress.WaitForExit(); byte[] result; using (var ddsStream = File.OpenRead(tempBitmap)) { header.Read(new EndianReader(ddsStream)); var dataSize = (int)(ddsStream.Length - ddsStream.Position); int mipMapCount = header.MipMapCount - 1; bitmap.Width = header.Width; bitmap.Height = header.Height; // Remove lowest DXN mipmaps to prevent issues with D3D memory allocation. if (bitmap.Format == BitmapFormat.Dxn) { dataSize = BitmapUtils.RoundSize(bitmap.Width, 4) * BitmapUtils.RoundSize(bitmap.Height, 4); if (mipMapCount > 0) { if (bitmap.Format == BitmapFormat.Dxn) { var width = bitmap.Width; var height = bitmap.Height; dataSize = BitmapUtils.RoundSize(width, 4) * BitmapUtils.RoundSize(height, 4); mipMapCount = 0; while ((width >= 8) && (height >= 8)) { width /= 2; height /= 2; dataSize += BitmapUtils.RoundSize(width, 4) * BitmapUtils.RoundSize(height, 4); mipMapCount++; } } } } bitmap.MipMapCount = mipMapCount; byte[] raw = new byte[dataSize]; ddsStream.Read(raw, 0, dataSize); result = raw; bitmap.Data = result; } if (File.Exists(tempBitmap)) { File.Delete(tempBitmap); } }
public override void EncodeToFile( Stream input, string outFileName, Codec.CodecData data ) { // Unwrap codecDataPtr - data is cleaned by calling function var imgData = (ImageData)data; // Check size for cube map faces var isCubeMap = imgData.size == Image.CalculateSize( imgData.numMipMaps, 6, imgData.width, imgData.height, imgData.depth, imgData.format ); // Establish texture attributes var isVolume = imgData.depth > 1; var isFloat32r = imgData.format == PixelFormat.FLOAT32_R; var hasAlpha = false; var notImplemented = false; var notImplementedString = string.Empty; // Check for all the 'not implemented' conditions if ( imgData.numMipMaps != 0 ) { // No mip map functionality yet notImplemented = true; notImplementedString += " mipmaps"; } if ( ( isVolume == true ) && ( imgData.width != imgData.height ) ) { // Square textures only notImplemented = true; notImplementedString += " non square textures"; } var size = 1; while ( size < imgData.width ) { size <<= 1; } if ( size != imgData.width ) { // Power two textures only notImplemented = true; notImplementedString += " non power two textures"; } switch ( imgData.format ) { case PixelFormat.A8R8G8B8: case PixelFormat.X8R8G8B8: case PixelFormat.R8G8B8: case PixelFormat.FLOAT32_R: break; default: // No crazy FOURCC or 565 et al. file formats at this stage notImplemented = true; notImplementedString = " unsupported pixel format"; break; } // Except if any 'not implemented' conditions were met if ( notImplemented ) { throw new NotImplementedException( string.Format( "DDS encoding for{0} not supported", notImplementedString ) ); } else { // Build header and write to disk // Variables for some DDS header flags var ddsHeaderFlags = 0; var ddsHeaderRgbBits = 0; var ddsHeaderSizeOrPitch = 0; var ddsHeaderCaps1 = 0; var ddsHeaderCaps2 = 0; var ddsMagic = this.DDS_MAGIC; // Initalise the header flags ddsHeaderFlags = ( isVolume ) ? DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_DEPTH | DDSD_PIXELFORMAT : DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; // Initalise the rgbBits flags switch ( imgData.format ) { case PixelFormat.A8R8G8B8: ddsHeaderRgbBits = 8*4; hasAlpha = true; break; case PixelFormat.X8R8G8B8: ddsHeaderRgbBits = 8*4; break; case PixelFormat.R8G8B8: ddsHeaderRgbBits = 8*3; break; case PixelFormat.FLOAT32_R: ddsHeaderRgbBits = 32; break; default: ddsHeaderRgbBits = 0; break; } ; // Initalise the SizeOrPitch flags (power two textures for now) ddsHeaderSizeOrPitch = ddsHeaderRgbBits*imgData.width; // Initalise the caps flags ddsHeaderCaps1 = ( isVolume || isCubeMap ) ? DDSCAPS_COMPLEX | DDSCAPS_TEXTURE : DDSCAPS_TEXTURE; if ( isVolume ) { ddsHeaderCaps2 = DDSCAPS2_VOLUME; } else if ( isCubeMap ) { ddsHeaderCaps2 = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX | DDSCAPS2_CUBEMAP_NEGATIVEX | DDSCAPS2_CUBEMAP_POSITIVEY | DDSCAPS2_CUBEMAP_NEGATIVEY | DDSCAPS2_CUBEMAP_POSITIVEZ | DDSCAPS2_CUBEMAP_NEGATIVEZ; } // Populate the DDS header information var ddsHeader = new DDSHeader(); ddsHeader.size = DDS_HEADER_SIZE; ddsHeader.flags = ddsHeaderFlags; ddsHeader.width = imgData.width; ddsHeader.height = imgData.height; ddsHeader.depth = isVolume ? imgData.depth : 0; ddsHeader.depth = isCubeMap ? 6 : ddsHeader.depth; ddsHeader.mipMapCount = 0; ddsHeader.sizeOrPitch = ddsHeaderSizeOrPitch; ddsHeader.reserved1 = new int[11]; ddsHeader.reserved2 = 0; ddsHeader.pixelFormat.size = DDS_PIXELFORMAT_SIZE; ddsHeader.pixelFormat.flags = ( hasAlpha ) ? DDPF_RGB | DDPF_ALPHAPIXELS : DDPF_RGB; ddsHeader.pixelFormat.flags = ( isFloat32r ) ? DDPF_FOURCC : ddsHeader.pixelFormat.flags; ddsHeader.pixelFormat.fourCC = ( isFloat32r ) ? D3DFMT_R32F : 0; ddsHeader.pixelFormat.rgbBits = ddsHeaderRgbBits; ddsHeader.pixelFormat.alphaMask = hasAlpha ? unchecked( (int)0xFF000000 ) : 0x00000000; ddsHeader.pixelFormat.alphaMask = isFloat32r ? 0x00000000 : ddsHeader.pixelFormat.alphaMask; ddsHeader.pixelFormat.redMask = isFloat32r ? unchecked( (int)0xFFFFFFFF ) : 0x00FF0000; ddsHeader.pixelFormat.greenMask = isFloat32r ? 0x00000000 : 0x0000FF00; ddsHeader.pixelFormat.blueMask = isFloat32r ? 0x00000000 : 0x000000FF; ddsHeader.caps.caps1 = ddsHeaderCaps1; ddsHeader.caps.caps2 = ddsHeaderCaps2; ddsHeader.caps.reserved[ 0 ] = 0; ddsHeader.caps.reserved[ 1 ] = 0; // Swap endian using ( var wrap = BufferBase.Wrap( ddsMagic, 2 ) ) { _flipEndian( wrap, sizeof ( uint ), 1 ); } using ( var wrap = BufferBase.Wrap( ddsHeader, Memory.SizeOf( typeof( DDSHeader ) ) ) ) { _flipEndian( wrap, 4, Memory.SizeOf( typeof ( DDSHeader ) )/4 ); } // Write the file using ( var br = new BinaryWriter( File.Open( outFileName, FileMode.OpenOrCreate, FileAccess.Write ) ) ) { br.Write( ddsMagic ); ddsHeader.Write( br ); // XXX flipEndian on each pixel chunk written unless isFloat32r ? var inputData = new byte[(int)input.Length]; input.Read( inputData, 0, inputData.Length ); br.Write( inputData ); } } }
public override void EncodeToFile(Stream input, string outFileName, Codec.CodecData data) { // Unwrap codecDataPtr - data is cleaned by calling function var imgData = (ImageData)data; // Check size for cube map faces var isCubeMap = imgData.size == Image.CalculateSize(imgData.numMipMaps, 6, imgData.width, imgData.height, imgData.depth, imgData.format); // Establish texture attributes var isVolume = imgData.depth > 1; var isFloat32r = imgData.format == PixelFormat.FLOAT32_R; var hasAlpha = false; var notImplemented = false; var notImplementedString = string.Empty; // Check for all the 'not implemented' conditions if (imgData.numMipMaps != 0) { // No mip map functionality yet notImplemented = true; notImplementedString += " mipmaps"; } if ((isVolume == true) && (imgData.width != imgData.height)) { // Square textures only notImplemented = true; notImplementedString += " non square textures"; } var size = 1; while (size < imgData.width) { size <<= 1; } if (size != imgData.width) { // Power two textures only notImplemented = true; notImplementedString += " non power two textures"; } switch (imgData.format) { case PixelFormat.A8R8G8B8: case PixelFormat.X8R8G8B8: case PixelFormat.R8G8B8: case PixelFormat.FLOAT32_R: break; default: // No crazy FOURCC or 565 et al. file formats at this stage notImplemented = true; notImplementedString = " unsupported pixel format"; break; } // Except if any 'not implemented' conditions were met if (notImplemented) { throw new NotImplementedException(string.Format("DDS encoding for{0} not supported", notImplementedString)); } else { // Build header and write to disk // Variables for some DDS header flags var ddsHeaderFlags = 0; var ddsHeaderRgbBits = 0; var ddsHeaderSizeOrPitch = 0; var ddsHeaderCaps1 = 0; var ddsHeaderCaps2 = 0; var ddsMagic = this.DDS_MAGIC; // Initalise the header flags ddsHeaderFlags = (isVolume) ? DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_DEPTH | DDSD_PIXELFORMAT : DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; // Initalise the rgbBits flags switch (imgData.format) { case PixelFormat.A8R8G8B8: ddsHeaderRgbBits = 8 * 4; hasAlpha = true; break; case PixelFormat.X8R8G8B8: ddsHeaderRgbBits = 8 * 4; break; case PixelFormat.R8G8B8: ddsHeaderRgbBits = 8 * 3; break; case PixelFormat.FLOAT32_R: ddsHeaderRgbBits = 32; break; default: ddsHeaderRgbBits = 0; break; } ; // Initalise the SizeOrPitch flags (power two textures for now) ddsHeaderSizeOrPitch = ddsHeaderRgbBits * imgData.width; // Initalise the caps flags ddsHeaderCaps1 = (isVolume || isCubeMap) ? DDSCAPS_COMPLEX | DDSCAPS_TEXTURE : DDSCAPS_TEXTURE; if (isVolume) { ddsHeaderCaps2 = DDSCAPS2_VOLUME; } else if (isCubeMap) { ddsHeaderCaps2 = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX | DDSCAPS2_CUBEMAP_NEGATIVEX | DDSCAPS2_CUBEMAP_POSITIVEY | DDSCAPS2_CUBEMAP_NEGATIVEY | DDSCAPS2_CUBEMAP_POSITIVEZ | DDSCAPS2_CUBEMAP_NEGATIVEZ; } // Populate the DDS header information var ddsHeader = new DDSHeader(); ddsHeader.size = DDS_HEADER_SIZE; ddsHeader.flags = ddsHeaderFlags; ddsHeader.width = imgData.width; ddsHeader.height = imgData.height; ddsHeader.depth = isVolume ? imgData.depth : 0; ddsHeader.depth = isCubeMap ? 6 : ddsHeader.depth; ddsHeader.mipMapCount = 0; ddsHeader.sizeOrPitch = ddsHeaderSizeOrPitch; ddsHeader.reserved1 = new int[11]; ddsHeader.reserved2 = 0; ddsHeader.pixelFormat.size = DDS_PIXELFORMAT_SIZE; ddsHeader.pixelFormat.flags = (hasAlpha) ? DDPF_RGB | DDPF_ALPHAPIXELS : DDPF_RGB; ddsHeader.pixelFormat.flags = (isFloat32r) ? DDPF_FOURCC : ddsHeader.pixelFormat.flags; ddsHeader.pixelFormat.fourCC = (isFloat32r) ? D3DFMT_R32F : 0; ddsHeader.pixelFormat.rgbBits = ddsHeaderRgbBits; ddsHeader.pixelFormat.alphaMask = hasAlpha ? unchecked ((int)0xFF000000) : 0x00000000; ddsHeader.pixelFormat.alphaMask = isFloat32r ? 0x00000000 : ddsHeader.pixelFormat.alphaMask; ddsHeader.pixelFormat.redMask = isFloat32r ? unchecked ((int)0xFFFFFFFF) : 0x00FF0000; ddsHeader.pixelFormat.greenMask = isFloat32r ? 0x00000000 : 0x0000FF00; ddsHeader.pixelFormat.blueMask = isFloat32r ? 0x00000000 : 0x000000FF; ddsHeader.caps.caps1 = ddsHeaderCaps1; ddsHeader.caps.caps2 = ddsHeaderCaps2; ddsHeader.caps.reserved[0] = 0; ddsHeader.caps.reserved[1] = 0; // Swap endian using (var wrap = BufferBase.Wrap(ddsMagic, 2)) { _flipEndian(wrap, sizeof(uint), 1); } using (var wrap = BufferBase.Wrap(ddsHeader, Memory.SizeOf(typeof(DDSHeader)))) { _flipEndian(wrap, 4, Memory.SizeOf(typeof(DDSHeader)) / 4); } // Write the file using (var br = new BinaryWriter(File.Open(outFileName, FileMode.OpenOrCreate, FileAccess.Write))) { br.Write(ddsMagic); ddsHeader.Write(br); // XXX flipEndian on each pixel chunk written unless isFloat32r ? var inputData = new byte[(int)input.Length]; input.Read(inputData, 0, inputData.Length); br.Write(inputData); } } }