protected override void InitWidgetFromToken(SaveConfigToken token) { if (token is DdsSaveConfigToken) { DdsSaveConfigToken ddsToken = (DdsSaveConfigToken)token; this.fileFormatList.SelectedIndex = ( int )ddsToken.m_fileFormat; this.clusterFit.Checked = (ddsToken.m_compressorType == 0); this.rangeFit.Checked = (ddsToken.m_compressorType == 1); this.iterativeFit.Checked = (ddsToken.m_compressorType == 2); this.perceptualMetric.Checked = (ddsToken.m_errorMetric == 0); this.uniformMetric.Checked = !this.perceptualMetric.Checked; this.weightColourByAlpha.Checked = ddsToken.m_weightColourByAlpha; this.generateMipMaps.Checked = ddsToken.m_generateMipMaps; } else { this.fileFormatList.SelectedIndex = 0; this.clusterFit.Checked = true; this.rangeFit.Checked = false; this.iterativeFit.Checked = false; this.perceptualMetric.Checked = true; this.uniformMetric.Checked = false; this.weightColourByAlpha.Checked = false; this.generateMipMaps.Checked = false; } }
protected DdsSaveConfigToken(DdsSaveConfigToken copyMe) { m_fileFormat = copyMe.m_fileFormat; m_compressorType = copyMe.m_compressorType; m_errorMetric = copyMe.m_errorMetric; m_weightColourByAlpha = copyMe.m_weightColourByAlpha; m_generateMipMaps = copyMe.m_generateMipMaps; }
protected override unsafe void OnSave(Document input, Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler callback) { DdsSaveConfigToken ddsToken = ( DdsSaveConfigToken )token; // We need to be able to feast on the goo inside.. scratchSurface.Clear(ColorBgra.Transparent); using (RenderArgs ra = new RenderArgs(scratchSurface)) { input.Render(ra, true); } // Create the DDS file, and save it.. DdsFile ddsFile = new DdsFile(); ddsFile.Save(output, scratchSurface, ddsToken, callback); }
public void Save(System.IO.Stream output, Surface surface, DdsSaveConfigToken ddsToken, ProgressEventHandler progressCallback) { // For non-compressed textures, we need pixel width. int pixelWidth = 0; // Identify if we're a compressed image bool isCompressed = ((ddsToken.m_fileFormat == DdsFileFormat.DDS_FORMAT_DXT1) || (ddsToken.m_fileFormat == DdsFileFormat.DDS_FORMAT_DXT3) || (ddsToken.m_fileFormat == DdsFileFormat.DDS_FORMAT_DXT5)); // Compute mip map count.. int mipCount = 1; int mipWidth = surface.Width; int mipHeight = surface.Height; if (ddsToken.m_generateMipMaps) { // This breaks! while ((mipWidth > 1) || (mipHeight > 1)) { mipCount++; mipWidth /= 2; mipHeight /= 2; } } // Populate bulk of our DdsHeader m_header.m_size = m_header.Size(); m_header.m_headerFlags = ( uint )(DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_TEXTURE); if (isCompressed) { m_header.m_headerFlags |= ( uint )(DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_LINEARSIZE); } else { m_header.m_headerFlags |= ( uint )(DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_PITCH); } if (mipCount > 1) { m_header.m_headerFlags |= ( uint )(DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_MIPMAP); } m_header.m_height = ( uint )surface.Height; m_header.m_width = ( uint )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 = ((surface.Width + 3) / 4) * ((surface.Height + 3) / 4); int blockSize = (ddsToken.m_fileFormat == 0) ? 8 : 16; m_header.m_pitchOrLinearSize = ( uint )(blockCount * blockSize); } else { // Non-compressed textures have the pitch flag set. So pitchOrLinearSize // needs to contain the row pitch of the main image. DWORD aligned too. switch (ddsToken.m_fileFormat) { case DdsFileFormat.DDS_FORMAT_A8R8G8B8: case DdsFileFormat.DDS_FORMAT_X8R8G8B8: case DdsFileFormat.DDS_FORMAT_A8B8G8R8: case DdsFileFormat.DDS_FORMAT_X8B8G8R8: pixelWidth = 4; // 32bpp break; case DdsFileFormat.DDS_FORMAT_A1R5G5B5: case DdsFileFormat.DDS_FORMAT_A4R4G4B4: case DdsFileFormat.DDS_FORMAT_R5G6B5: pixelWidth = 2; // 16bpp break; case DdsFileFormat.DDS_FORMAT_R8G8B8: pixelWidth = 3; // 24bpp break; } // Compute row pitch m_header.m_pitchOrLinearSize = ( uint )(( int )m_header.m_width * pixelWidth); #if APPLY_PITCH_ALIGNMENT // Align to DWORD, if we need to.. (see notes about pitch alignment all over this code) m_header.m_pitchOrLinearSize = ( uint )((( int )m_header.m_pitchOrLinearSize + 3) & (~3)); #endif //APPLY_PITCH_ALIGNMENT } m_header.m_depth = 0; m_header.m_mipMapCount = (mipCount == 1) ? 0 : ( uint )mipCount; m_header.m_reserved1_0 = 0; m_header.m_reserved1_1 = 0; m_header.m_reserved1_2 = 0; m_header.m_reserved1_3 = 0; m_header.m_reserved1_4 = 0; m_header.m_reserved1_5 = 0; m_header.m_reserved1_6 = 0; m_header.m_reserved1_7 = 0; m_header.m_reserved1_8 = 0; m_header.m_reserved1_9 = 0; m_header.m_reserved1_10 = 0; // Populate our DdsPixelFormat object m_header.m_pixelFormat.Initialise(ddsToken.m_fileFormat); // Populate miscellanous header flags m_header.m_surfaceFlags = ( uint )DdsHeader.SurfaceFlags.DDS_SURFACE_FLAGS_TEXTURE; if (mipCount > 1) { m_header.m_surfaceFlags |= ( uint )DdsHeader.SurfaceFlags.DDS_SURFACE_FLAGS_MIPMAP; } m_header.m_cubemapFlags = 0; m_header.m_reserved2_0 = 0; m_header.m_reserved2_1 = 0; m_header.m_reserved2_2 = 0; // Write out our DDS tag Utility.WriteUInt32(output, 0x20534444); // 'DDS ' // Write out the header m_header.Write(output); int squishFlags = ddsToken.GetSquishFlags(); // Our output data array will be sized as necessary byte[] outputData; // Reset our mip width & height variables... mipWidth = surface.Width; mipHeight = 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 = surface.Width; mipHeight = surface.Height; for (int mipLoop = 0; mipLoop < mipCount; 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 { 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(outputData, 0, outputData.GetLength(0)); mipWidth = mipWidth / 2; mipHeight = mipHeight / 2; } }
public void Save( System.IO.Stream output, Surface surface, DdsSaveConfigToken ddsToken, ProgressEventHandler progressCallback ) { // For non-compressed textures, we need pixel width. int pixelWidth = 0; // Identify if we're a compressed image bool isCompressed = ( ( ddsToken.m_fileFormat == DdsFileFormat.DDS_FORMAT_DXT1 ) || ( ddsToken.m_fileFormat == DdsFileFormat.DDS_FORMAT_DXT3 ) || ( ddsToken.m_fileFormat == DdsFileFormat.DDS_FORMAT_DXT5 ) ); // Compute mip map count.. int mipCount = 1; int mipWidth = surface.Width; int mipHeight = surface.Height; if ( ddsToken.m_generateMipMaps ) { // This breaks! while ( ( mipWidth > 1 ) || ( mipHeight > 1 ) ) { mipCount++; mipWidth /= 2; mipHeight /= 2; } } // Populate bulk of our DdsHeader m_header.m_size = m_header.Size(); m_header.m_headerFlags = ( uint )( DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_TEXTURE ); if ( isCompressed ) m_header.m_headerFlags |= ( uint )( DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_LINEARSIZE ); else m_header.m_headerFlags |= ( uint )( DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_PITCH ); if ( mipCount > 1 ) m_header.m_headerFlags |= ( uint )( DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_MIPMAP ); m_header.m_height = ( uint )surface.Height; m_header.m_width = ( uint )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 = ( ( surface.Width + 3 )/4 ) * ( ( surface.Height + 3 )/4 ); int blockSize = ( ddsToken.m_fileFormat == 0 ) ? 8 : 16; m_header.m_pitchOrLinearSize = ( uint )( blockCount * blockSize ); } else { // Non-compressed textures have the pitch flag set. So pitchOrLinearSize // needs to contain the row pitch of the main image. DWORD aligned too. switch ( ddsToken.m_fileFormat ) { case DdsFileFormat.DDS_FORMAT_A8R8G8B8: case DdsFileFormat.DDS_FORMAT_X8R8G8B8: case DdsFileFormat.DDS_FORMAT_A8B8G8R8: case DdsFileFormat.DDS_FORMAT_X8B8G8R8: pixelWidth = 4; // 32bpp break; case DdsFileFormat.DDS_FORMAT_A1R5G5B5: case DdsFileFormat.DDS_FORMAT_A4R4G4B4: case DdsFileFormat.DDS_FORMAT_R5G6B5: pixelWidth = 2; // 16bpp break; case DdsFileFormat.DDS_FORMAT_R8G8B8: pixelWidth = 3; // 24bpp break; } // Compute row pitch m_header.m_pitchOrLinearSize = ( uint )( ( int )m_header.m_width * pixelWidth ); #if APPLY_PITCH_ALIGNMENT // Align to DWORD, if we need to.. (see notes about pitch alignment all over this code) m_header.m_pitchOrLinearSize = ( uint )( ( ( int )m_header.m_pitchOrLinearSize + 3 ) & ( ~3 ) ); #endif //APPLY_PITCH_ALIGNMENT } m_header.m_depth = 0; m_header.m_mipMapCount = ( mipCount == 1 ) ? 0 : ( uint )mipCount; m_header.m_reserved1_0 = 0; m_header.m_reserved1_1 = 0; m_header.m_reserved1_2 = 0; m_header.m_reserved1_3 = 0; m_header.m_reserved1_4 = 0; m_header.m_reserved1_5 = 0; m_header.m_reserved1_6 = 0; m_header.m_reserved1_7 = 0; m_header.m_reserved1_8 = 0; m_header.m_reserved1_9 = 0; m_header.m_reserved1_10 = 0; // Populate our DdsPixelFormat object m_header.m_pixelFormat.Initialise( ddsToken.m_fileFormat ); // Populate miscellanous header flags m_header.m_surfaceFlags = ( uint )DdsHeader.SurfaceFlags.DDS_SURFACE_FLAGS_TEXTURE; if ( mipCount > 1 ) m_header.m_surfaceFlags |= ( uint )DdsHeader.SurfaceFlags.DDS_SURFACE_FLAGS_MIPMAP; m_header.m_cubemapFlags = 0; m_header.m_reserved2_0 = 0; m_header.m_reserved2_1 = 0; m_header.m_reserved2_2 = 0; // Write out our DDS tag Utility.WriteUInt32( output, 0x20534444 ); // 'DDS ' // Write out the header m_header.Write( output ); int squishFlags = ddsToken.GetSquishFlags(); // Our output data array will be sized as necessary byte[] outputData; // Reset our mip width & height variables... mipWidth = surface.Width; mipHeight = 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 = surface.Width; mipHeight = surface.Height; for (int mipLoop = 0; mipLoop < mipCount; 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 { 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( outputData, 0, outputData.GetLength( 0 ) ); mipWidth = mipWidth / 2; mipHeight = mipHeight / 2; } }
protected DdsSaveConfigToken( DdsSaveConfigToken copyMe ) { m_fileFormat = copyMe.m_fileFormat; m_compressorType = copyMe.m_compressorType; m_errorMetric = copyMe.m_errorMetric; m_weightColourByAlpha = copyMe.m_weightColourByAlpha; m_generateMipMaps = copyMe.m_generateMipMaps; }