private Stream buildDdsImage(int mipMapIndex, out FileFormat imageFormat) { DomainPropertyByteValue formatProp = PropertyHeader.GetProperty("Format").FirstOrDefault()?.Value as DomainPropertyByteValue; imageFormat = FileFormat.Unknown; if (formatProp == null) { return(null); } string format = formatProp.PropertyString; DomainMipMap mipMap = MipMaps[mipMapIndex]; imageFormat = DdsPixelFormat.ParseFileFormat(format); DdsHeader ddsHeader = new DdsHeader(new DdsSaveConfig(imageFormat, 0, 0, false, false), mipMap.Width, mipMap.Height); MemoryStream stream = new MemoryStream(); BinaryWriter writer = new BinaryWriter(stream); ddsHeader.Write(writer); stream.Write(mipMap.ImageData, 0, mipMap.ImageData.Length); stream.Flush(); stream.Position = 0; return(stream); }
public static unsafe bool RgbaBytesToDds(byte[] rgba, int width, int height, out byte[] ddsData) { var header = new DdsHeader() { Caps1 = DdsHeader.DdsCaps1.Complex | DdsHeader.DdsCaps1.Texture | DdsHeader.DdsCaps1.MipMap, Depth = 1, Flags = DdsHeader.DdsFlags.Required | DdsHeader.DdsFlags.Pitch | DdsHeader.DdsFlags.MipMapCount, Height = height, Width = width, PixelFormat = new PixelFormat() { Flags = PixelFormat.FormatFlags.AlphaPixels | PixelFormat.FormatFlags.RGB, FourCC = 0, BBitMask = 0x000000FF, GBitMask = 0x0000FF00, RBitMask = 0x00FF0000, ABitMask = 0xFF000000, Size = 32, RgbBitCount = 32, }, }; ddsData = new byte[4 + DdsHeader.Size + rgba.Length]; header.Write(ddsData, 0); rgba.CopyTo(ddsData, DdsHeader.Size + 4); for (var i = 0; i < rgba.Length; i += 4) { (ddsData[DdsHeader.Size + i], ddsData[DdsHeader.Size + i + 2]) = (ddsData[DdsHeader.Size + i + 2], ddsData[DdsHeader.Size + i]); } return(true); }
private Stream buildDdsImage(int mipMapIndex) { MipMap mipMap = maps[mipMapIndex]; DdsHeader ddsHeader = new DdsHeader(new DdsSaveConfig(parsedImageFormat, 0, 0, false, false), mipMap.sizeX, mipMap.sizeY); MemoryStream stream = new MemoryStream(); BinaryWriter writer = new BinaryWriter(stream); ddsHeader.Write(writer); writer.Write(mipMap.uncompressedData); writer.Flush(); writer.BaseStream.Position = 0; return(stream); }
private Stream buildDdsImage(int mipMapIndex, out FileFormat imageFormat) { MipMap mipMap = maps[mipMapIndex]; imageFormat = GetFormat(); DdsHeader ddsHeader = new DdsHeader(new DdsSaveConfig(imageFormat, 0, 0, false, false), mipMap.sizeX, mipMap.sizeY); MemoryStream stream = new MemoryStream(); BinaryWriter writer = new BinaryWriter(stream); ddsHeader.Write(writer); stream.Write(mipMap.uncompressedData, 0, mipMap.uncompressedData.Length); stream.Flush(); stream.Position = 0; return(stream); }
internal void Save(TextureCollection textures, Stream output, ProgressEventHandler progressCallback) { DdsHeader header = new DdsHeader(this.width, this.height, this.arraySize, this.mipLevels, this.format); output.WriteUInt32(DdsMagic); header.Write(output); double progressTotal = this.arraySize * this.mipLevels; for (int i = 0; i < this.arraySize; ++i) { int startIndex = i * this.mipLevels; for (int j = 0; j < this.mipLevels; ++j) { int index = startIndex + j; WritePixelData(textures[index].Surface, output); progressCallback?.Invoke(this, new ProgressEventArgs((index / progressTotal) * 100.0, true)); } } }
////////////////////////////////////////////// public static void WriteFile(Stream output, DDSTextureTools.DDSImage image) { DdsHeader header = new DdsHeader(); // For non-compressed textures, we need pixel width. //int pixelWidth = 0; // Identify if we're a compressed image bool isCompressed = image.Format == DDSTextureTools.DDSImage.FormatEnum.DXT1 || image.Format == DDSTextureTools.DDSImage.FormatEnum.DXT3 || image.Format == DDSTextureTools.DDSImage.FormatEnum.DXT5 || image.Format == DDSTextureTools.DDSImage.FormatEnum.BC5; int width = image.Surfaces[0].Size.X; int height = image.Surfaces[0].Size.Y; //// Compute mip map count.. int mipCount = image.Cubemap ? image.Surfaces.Length / 6 : image.Surfaces.Length; int mipWidth = width; // surface.Width; int mipHeight = height; // surface.Height; // Populate bulk of our DdsHeader header.m_size = header.Size(); header.m_headerFlags = (uint)(DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_TEXTURE); if (isCompressed) { header.m_headerFlags |= (uint)(DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_LINEARSIZE); } else { header.m_headerFlags |= (uint)(DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_PITCH); } if (mipCount > 1) { header.m_headerFlags |= (uint)(DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_MIPMAP); } header.m_height = (uint)height; // surface.Height; header.m_width = (uint)width; // surface.Width; if (isCompressed) { // Compresssed textures have the linear flag set.So pitchOrLinearSize // needs to contain the entire size of the DXT block. int blockCount = ((width + 3) / 4) * ((height + 3) / 4); int blockSize = (image.Format == DDSTextureTools.DDSImage.FormatEnum.DXT1) ? 8 : 16; header.m_pitchOrLinearSize = (uint)(blockCount * blockSize); } else { int pixelWidth = 0; // Non-compressed textures have the pitch flag set. So pitchOrLinearSize // needs to contain the row pitch of the main image. DWORD aligned too. switch (image.Format) { case DDSTextureTools.DDSImage.FormatEnum.A8R8G8B8: case DDSTextureTools.DDSImage.FormatEnum.X8R8G8B8: case DDSTextureTools.DDSImage.FormatEnum.A8B8G8R8: case DDSTextureTools.DDSImage.FormatEnum.X8B8G8R8: pixelWidth = 4; // 32bpp break; case DDSTextureTools.DDSImage.FormatEnum.A1R5G5B5: case DDSTextureTools.DDSImage.FormatEnum.A4R4G4B4: case DDSTextureTools.DDSImage.FormatEnum.R5G6B5: pixelWidth = 2; // 16bpp break; case DDSTextureTools.DDSImage.FormatEnum.R8G8B8: pixelWidth = 3; // 24bpp break; case DDSTextureTools.DDSImage.FormatEnum.R16G16B16A16: pixelWidth = 8; // 64bpp break; } // Compute row pitch header.m_pitchOrLinearSize = (uint)((int)header.m_width * pixelWidth); ////#if APPLY_PITCH_ALIGNMENT //// Align to DWORD, if we need to.. (see notes about pitch alignment all over this code) //header.m_pitchOrLinearSize = (uint)( ( (int)header.m_pitchOrLinearSize + 3 ) & ( ~3 ) ); ////#endif //APPLY_PITCH_ALIGNMENT } header.m_depth = 0; header.m_mipMapCount = (mipCount == 1) ? 0 : (uint)mipCount; header.m_reserved1_0 = 0; header.m_reserved1_1 = 0; header.m_reserved1_2 = 0; header.m_reserved1_3 = 0; header.m_reserved1_4 = 0; header.m_reserved1_5 = 0; header.m_reserved1_6 = 0; header.m_reserved1_7 = 0; header.m_reserved1_8 = 0; header.m_reserved1_9 = 0; header.m_reserved1_10 = 0; // Populate our DdsPixelFormat object header.m_pixelFormat.Initialise(image.Format); // Populate miscellanous header flags header.m_surfaceFlags = (uint)DdsHeader.SurfaceFlags.DDS_SURFACE_FLAGS_TEXTURE; if (mipCount > 1) { header.m_surfaceFlags |= (uint)DdsHeader.SurfaceFlags.DDS_SURFACE_FLAGS_MIPMAP; } if (image.Cubemap) { header.m_surfaceFlags |= (uint)DdsHeader.SurfaceFlags.DDS_SURFACE_FLAGS_CUBEMAP; } header.m_cubemapFlags = image.Cubemap ? (uint)DdsHeader.CubemapFlags.DDS_CUBEMAP_ALLFACES : 0; header.m_reserved2_0 = 0; header.m_reserved2_1 = 0; header.m_reserved2_2 = 0; // Write out our DDS tag Utility.WriteUInt32(output, 0x20534444); // 'DDS ' // Write out the header header.Write(output); //DDSSquish.SquishFlags squishFlags = /*ddsToken.*/GetSquishFlags( fileFormat ); //// Our output data array will be sized as necessary //byte[] outputData; // Reset our mip width & height variables... mipWidth = width; // surface.Width; mipHeight = height; // surface.Height; //// Figure out how much total work each mip map is //Size[] writeSizes = new Size[ mipCount ]; //int[] mipPixels = new int[ mipCount ]; //int[] pixelsCompleted = new int[ mipCount ]; // # pixels completed once we have reached this mip //long totalPixels = 0; //for( int mipLoop = 0; mipLoop < mipCount; mipLoop++ ) //{ // Size writeSize = new Size( ( mipWidth > 0 ) ? mipWidth : 1, ( mipHeight > 0 ) ? mipHeight : 1 ); // writeSizes[ mipLoop ] = writeSize; // int thisMipPixels = writeSize.Width * writeSize.Height; // mipPixels[ mipLoop ] = thisMipPixels; // if( mipLoop == 0 ) // { // pixelsCompleted[ mipLoop ] = 0; // } // else // { // pixelsCompleted[ mipLoop ] = pixelsCompleted[ mipLoop - 1 ] + mipPixels[ mipLoop - 1 ]; // } // totalPixels += thisMipPixels; // mipWidth /= 2; // mipHeight /= 2; //} //mipWidth = width;// surface.Width; //mipHeight = height;// surface.Height; for (int nSurface = 0; nSurface < image.Surfaces.Length; nSurface++) //for( int mipLoop = 0; mipLoop < mipCount; mipLoop++ ) { DDSTextureTools.DDSImage.Surface surface = image.Surfaces[nSurface]; //TextureTools.DDSImage.Surface surface = image.Surfaces[ mipLoop ]; //Size writeSize = writeSizes[ mipLoop ]; //Surface writeSurface = new Surface( writeSize ); //if( mipLoop == 0 ) //{ // // No point resampling the first level.. it's got exactly what we want. // writeSurface = surface; //} //else //{ // // I'd love to have a UI component to select what kind of resampling, but // // there's hardly any space for custom UI stuff in the Save Dialog. And I'm // // not having any scrollbars in there..! // // Also, note that each mip level is formed from the main level, to reduce // // compounded errors when generating mips. // writeSurface.SuperSamplingFitSurface( surface ); //} //DdsSquish.ProgressFn progressFn = // delegate( int workDone, int workTotal ) // { // long thisMipPixelsDone = workDone * (long)mipWidth; // long previousMipsPixelsDone = pixelsCompleted[ mipLoop ]; // double progress = (double)( (double)thisMipPixelsDone + (double)previousMipsPixelsDone ) / (double)totalPixels; // progressCallback( this, new ProgressEventArgs( 100.0 * progress ) ); // }; //if( ( /*ddsToken.m_*/fileFormat >= DdsFileFormat.DDS_FORMAT_DXT1 ) && // ( /*ddsToken.m_*/fileFormat <= DdsFileFormat.DDS_FORMAT_DXT5 ) ) //{ // outputData = DDSSquish.CompressImage( writeSurface, squishFlags/*, // ( progressCallback == null ) ? null : progressFn*/ // ); //} //else //{ // Trace.Assert( false ); // int mipPitch = pixelWidth * writeSurface.Width; // // From the DDS documents I read, I'd expected the pitch of each mip level to be // // DWORD aligned. As it happens, that's not the case. Re-aligning the pitch of // // each level results in later mips getting sheared as the pitch is incorrect. // // So, the following line is intentionally optional. Maybe the documentation // // is referring to the pitch when accessing the mip directly.. who knows. // // // // Infact, all the talk of non-compressed textures having DWORD alignment of pitch // // seems to be bollocks.. If I apply alignment, then they fail to load in 3rd Party // // or Microsoft DDS viewing applications. // // //#if APPLY_PITCH_ALIGNMENT // mipPitch = ( mipPitch + 3 ) & ( ~3 ); //#endif // APPLY_PITCH_ALIGNMENT // outputData = new byte[ mipPitch * writeSurface.Height ]; // outputData.Initialize(); // for( int y = 0; y < writeSurface.Height; y++ ) // { // for( int x = 0; x < writeSurface.Width; x++ ) // { // // Get colour from surface // ColorBgra pixelColour = writeSurface.GetPoint( x, y ); // uint pixelData = 0; // switch( ddsToken.m_fileFormat ) // { // case DdsFileFormat.DDS_FORMAT_A8R8G8B8: // { // pixelData = ( (uint)pixelColour.A << 24 ) | // ( (uint)pixelColour.R << 16 ) | // ( (uint)pixelColour.G << 8 ) | // ( (uint)pixelColour.B << 0 ); // break; // } // case DdsFileFormat.DDS_FORMAT_X8R8G8B8: // { // pixelData = ( (uint)pixelColour.R << 16 ) | // ( (uint)pixelColour.G << 8 ) | // ( (uint)pixelColour.B << 0 ); // break; // } // case DdsFileFormat.DDS_FORMAT_A8B8G8R8: // { // pixelData = ( (uint)pixelColour.A << 24 ) | // ( (uint)pixelColour.B << 16 ) | // ( (uint)pixelColour.G << 8 ) | // ( (uint)pixelColour.R << 0 ); // break; // } // case DdsFileFormat.DDS_FORMAT_X8B8G8R8: // { // pixelData = ( (uint)pixelColour.B << 16 ) | // ( (uint)pixelColour.G << 8 ) | // ( (uint)pixelColour.R << 0 ); // break; // } // case DdsFileFormat.DDS_FORMAT_A1R5G5B5: // { // pixelData = ( (uint)( ( pixelColour.A != 0 ) ? 1 : 0 ) << 15 ) | // ( (uint)( pixelColour.R >> 3 ) << 10 ) | // ( (uint)( pixelColour.G >> 3 ) << 5 ) | // ( (uint)( pixelColour.B >> 3 ) << 0 ); // break; // } // case DdsFileFormat.DDS_FORMAT_A4R4G4B4: // { // pixelData = ( (uint)( pixelColour.A >> 4 ) << 12 ) | // ( (uint)( pixelColour.R >> 4 ) << 8 ) | // ( (uint)( pixelColour.G >> 4 ) << 4 ) | // ( (uint)( pixelColour.B >> 4 ) << 0 ); // break; // } // case DdsFileFormat.DDS_FORMAT_R8G8B8: // { // pixelData = ( (uint)pixelColour.R << 16 ) | // ( (uint)pixelColour.G << 8 ) | // ( (uint)pixelColour.B << 0 ); // break; // } // case DdsFileFormat.DDS_FORMAT_R5G6B5: // { // pixelData = ( (uint)( pixelColour.R >> 3 ) << 11 ) | // ( (uint)( pixelColour.G >> 2 ) << 5 ) | // ( (uint)( pixelColour.B >> 3 ) << 0 ); // break; // } // } // // pixelData contains our target data.. so now set the pixel bytes // int pixelOffset = ( y * mipPitch ) + ( x * pixelWidth ); // for( int loop = 0; loop < pixelWidth; loop++ ) // { // outputData[ pixelOffset + loop ] = (byte)( ( pixelData >> ( 8 * loop ) ) & 0xff ); // } // } // if( progressCallback != null ) // { // long thisMipPixelsDone = ( y + 1 ) * (long)mipWidth; // long previousMipsPixelsDone = pixelsCompleted[ mipLoop ]; // double progress = (double)( (double)thisMipPixelsDone + (double)previousMipsPixelsDone ) / (double)totalPixels; // progressCallback( this, new ProgressEventArgs( 100.0 * progress ) ); // } // } //} // Write the data for this mip level out.. output.Write(surface.Data, 0, surface.Data.Length); //output.Write( outputData, 0, outputData.GetLength( 0 ) ); //mipWidth = mipWidth / 2; //mipHeight = mipHeight / 2; } }