private void ImportTexture(string name, TextureProcessingSettings settings) { string destinationPath = Path.Combine(destinationFolder.FullName, name + ".dds"); if (new FileInfo(destinationPath).Exists) { return; } Console.WriteLine($"importing texture '{name}'..."); using (var image = UnmanagedRgbaImage.Load(settings.File)) { using (var dilator = new TextureDilator(device, shaderCache)) { dilator.Dilate(settings.Mask, image.Size, settings.IsLinear, image.DataBox); } InputOptions input = new InputOptions(); input.SetFormat(InputFormat.BGRA_8UB); input.SetTextureLayout(TextureType.Texture2D, image.Size.Width, image.Size.Height, 1); float gamma = settings.IsLinear ? 1f : 2.2f; input.SetGamma(gamma, gamma); input.SetMipmapData(image.PixelData, image.Size.Width, image.Size.Height, 1, 0, 0); input.SetAlphaMode(AlphaMode.None); input.SetMipmapGeneration(true); input.SetMipmapFilter(MipmapFilter.Kaiser); input.SetKaiserParameters(3, 4, 1); if (settings.Type == TextureProcessingType.Bump) { input.SetConvertToNormalMap(true); input.SetNormalFilter(1, 0, 0, 0); input.SetHeightEvaluation(1, 1, 1, 0); } else if (settings.Type == TextureProcessingType.Normal) { input.SetNormalMap(true); } CompressionOptions compression = new CompressionOptions(); compression.SetQuality(Quality.Highest); compression.SetFormat(Format.RGBA); OutputOptions output = new OutputOptions(); destinationFolder.CreateWithParents(); output.SetFileName(destinationPath); output.SetContainer(Container.Container_DDS10); output.SetSrgbFlag(!settings.IsLinear); var compressor = new Compressor(); bool succeeded = compressor.Compress(input, compression, output); if (!succeeded) { throw new InvalidOperationException("texture conversion failed"); } //force the previous output handler to be destructed so that the file is flushed and closed output.SetFileName("nul"); if (compress) { CompressTexture(new FileInfo(destinationPath), settings.Type, settings.IsLinear); } } }
protected override bool TryCopyFrom(BitmapContent sourceBitmap, Rectangle sourceRegion, Rectangle destinationRegion) { if (!sourceBitmap.TryGetFormat(out SurfaceFormat sourceFormat)) { return(false); } TryGetFormat(out SurfaceFormat format); // A shortcut for copying the entire bitmap to another bitmap of the same type and format if (format == sourceFormat && sourceRegion == new Rectangle(0, 0, Width, Height) && sourceRegion == destinationRegion) { SetPixelData(sourceBitmap.GetPixelData()); return(true); } // TODO: Add a XNA unit test to see what it does // my guess is that this is invalid for DXT. // // Destination region copy is not yet supported if (destinationRegion != new Rectangle(0, 0, Width, Height)) { return(false); } if (sourceBitmap is PixelBitmapContent <RgbaVector> && sourceRegion.Width == destinationRegion.Width && sourceRegion.Height == destinationRegion.Height) { // NVTT wants 8bit data in BGRA format. var colorBitmap = new PixelBitmapContent <Bgra32>(sourceBitmap.Width, sourceBitmap.Height); Copy(sourceBitmap, colorBitmap); var sourceData = colorBitmap.GetPixelData(); AlphaMode alphaMode; Format outputFormat; bool alphaDither = false; switch (format) { case SurfaceFormat.Dxt1: case SurfaceFormat.Dxt1SRgb: { PrepareNVTT_DXT1(sourceData, out bool hasTransparency); outputFormat = hasTransparency ? Format.DXT1a : Format.DXT1; alphaMode = hasTransparency ? AlphaMode.Transparency : AlphaMode.None; alphaDither = true; break; } case SurfaceFormat.Dxt3: case SurfaceFormat.Dxt3SRgb: { //PrepareNVTT(sourceData); outputFormat = Format.DXT3; alphaMode = AlphaMode.Transparency; break; } case SurfaceFormat.Dxt5: case SurfaceFormat.Dxt5SRgb: { //PrepareNVTT(sourceData); outputFormat = Format.DXT5; alphaMode = AlphaMode.Transparency; break; } default: throw new InvalidOperationException("Invalid DXT surface format!"); } // Do all the calls to the NVTT wrapper within this handler // so we properly clean up if things blow up. var dataHandle = GCHandle.Alloc(sourceData, GCHandleType.Pinned); try { var dataPtr = dataHandle.AddrOfPinnedObject(); var inputOptions = new InputOptions(); inputOptions.SetTextureLayout(TextureType.Texture2D, colorBitmap.Width, colorBitmap.Height, 1); inputOptions.SetMipmapData(dataPtr, colorBitmap.Width, colorBitmap.Height, 1, 0, 0); inputOptions.SetMipmapGeneration(false); inputOptions.SetGamma(1.0f, 1.0f); inputOptions.SetAlphaMode(alphaMode); var compressionOptions = new CompressionOptions(); compressionOptions.SetFormat(outputFormat); compressionOptions.SetQuality(Quality.Normal); // TODO: This isn't working which keeps us from getting the // same alpha dither behavior on DXT1 as XNA. // // See https://github.com/MonoGame/MonoGame/issues/6259 // //if (alphaDither) //compressionOptions.SetQuantization(false, false, true); var outputOptions = new OutputOptions(); outputOptions.SetOutputHeader(false); outputOptions.SetOutputOptionsOutputHandler(NvttBeginImage, NvttWriteImage, NvttEndImage); var dxtCompressor = new Compressor(); dxtCompressor.Compress(inputOptions, compressionOptions, outputOptions); } finally { dataHandle.Free(); } return(true); } try { Copy(sourceBitmap, sourceRegion, this, destinationRegion); return(true); } catch (InvalidOperationException) { return(false); } }
public static unsafe bool Compress(this Texture2DInfo tex, TextureFormat format) { var data = tex.RawData; int width = tex.Width, height = tex.Height, pos, outPos; switch (format) { case TextureFormat.Alpha8: { pos = 0; outPos = 0; var out_ = new byte[width * height]; fixed(byte *pOut = out_, pData = data) for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { var fR = pData[pos]; var fG = pData[pos + 1]; var fB = pData[pos + 2]; var fA = pData[pos + 3]; pOut[outPos] = (byte)(((fR + fB + fG) / 3) & 0XFF); pos += 4; outPos += 1; } } tex.RawData = out_; return(true); } case TextureFormat.ARGB4444: case TextureFormat.RGB565: { pos = 0; outPos = 0; using (var pvrTexture = PVRTexture.CreateTexture(data, (uint)width, (uint)height, 1, PixelFormat.RGBA8888, true, VariableType.UnsignedByte, ColourSpace.sRGB)) { var doDither = true; pvrTexture.Transcode(PixelFormat.RGBA4444, VariableType.UnsignedByte, ColourSpace.sRGB, format == TextureFormat.ARGB4444 ? CompressorQuality.PVRTCNormal : CompressorQuality.ETCMedium, doDither); var texDataSize = pvrTexture.GetTextureDataSize(0); var out_ = new byte[texDataSize]; pvrTexture.GetTextureData(out_, texDataSize); if (format == TextureFormat.RGB565) { var data2 = out_; out_ = new byte[texDataSize]; fixed(byte *pOut = out_, pData = data2) for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { var v0 = pData[pos]; var v1 = pData[pos + 1]; // 4bit little endian {A, B},{G, R} var sA = v0 & 0xF0 >> 4; var sB = (v0 & 0xF0) >> 4; var sG = v1 & 0xF0 >> 4; var sR = (v1 & 0xF0) >> 4; // swap to little endian {B, G, R, A } var fB = sB & 0xf; var fG = sG & 0xf; var fR = sR & 0xf; var fA = sA & 0xf; pOut[outPos] = (byte)((fG << 4) + fB); pOut[outPos + 1] = (byte)((fA << 4) + fR); pos += 2; outPos += 2; } } } tex.RawData = out_; return(true); } } case TextureFormat.RGB24: { pos = 0; outPos = 0; var out_ = new byte[width * height * 3]; fixed(byte *pOut = out_, pData = data) for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { // 4bit little endian {A, B},{G, R} var fR = pData[pos]; var fG = pData[pos + 1]; var fB = pData[pos + 2]; var fA = pData[pos + 3]; pOut[outPos] = fR; pOut[outPos + 1] = fG; pOut[outPos + 2] = fB; pos += 4; outPos += 3; } } tex.RawData = out_; return(true); } case TextureFormat.RGBA32: return(true); case TextureFormat.ARGB32: { pos = 0; outPos = 0; var out_ = new byte[width * height * 4]; fixed(byte *pOut = out_, pData = data) for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { var fA = pData[pos]; var fR = pData[pos + 1]; var fG = pData[pos + 2]; var fB = pData[pos + 3]; out_[outPos] = fR; out_[outPos + 1] = fG; out_[outPos + 2] = fB; out_[outPos + 3] = fA; pos += 4; outPos += 4; } } tex.RawData = out_; return(true); } //case TextureFormat.ATC_RGBA8: //case TextureFormat.ATC_RGB4: case TextureFormat.ETC2_RGBA8: case TextureFormat.ETC_RGB4: TextureConverterWrapper.CompressionFormat format2; switch (format) { //case TextureFormat.ATC_RGBA8: format2 = TextureConverterWrapper.CompressionFormat.AtcRgbaExplicitAlpha; break; //case TextureFormat.ATC_RGB4: format2 = TextureConverterWrapper.CompressionFormat.AtcRgb; break; case TextureFormat.ETC2_RGBA8: format2 = TextureConverterWrapper.CompressionFormat.Etc2Rgba; break; case TextureFormat.ETC_RGB4: format2 = TextureConverterWrapper.CompressionFormat.Etc1; break; default: throw new ArgumentOutOfRangeException(nameof(format), format.ToString()); } tex.RawData = TextureConverterWrapper.Compress(data, width, height, format2); return(true); case TextureFormat.DXT1: case TextureFormat.DXT5: { var dxtCompressor = new Compressor(); var inputOptions = new InputOptions(); inputOptions.SetAlphaMode(format == TextureFormat.DXT1 ? AlphaMode.None : AlphaMode.Premultiplied); inputOptions.SetTextureLayout(TextureType.Texture2D, width, height, 1); fixed(byte *pData = data) for (var x = 0; x < data.Length; x += 4) { pData[x] ^= pData[x + 2]; pData[x + 2] ^= pData[x]; pData[x] ^= pData[x + 2]; } var dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned); try { var dataPtr = dataHandle.AddrOfPinnedObject(); inputOptions.SetMipmapData(dataPtr, width, height, 1, 0, 0); inputOptions.SetMipmapGeneration(false); inputOptions.SetGamma(1.0f, 1.0f); var compressionOptions = new CompressionOptions(); compressionOptions.SetFormat(format == TextureFormat.DXT1 ? Nvidia.TextureTools.Format.DXT1 : Nvidia.TextureTools.Format.DXT5); compressionOptions.SetQuality(Quality.Normal); var outputOptions = new OutputOptions(); outputOptions.SetOutputHeader(false); var out_ = new byte[0]; using (var handler = new DxtDataHandler(out_, outputOptions)) { dxtCompressor.Compress(inputOptions, compressionOptions, outputOptions); out_ = handler.dst; } tex.RawData = out_; return(true); } finally { dataHandle.Free(); } } case TextureFormat.ASTC_RGBA_4x4: case TextureFormat.ASTC_RGB_4x4: AstcencWrapper.EncodeASTC(data, width, height, 4, 4, out tex.RawData); return(true); default: throw new ArgumentOutOfRangeException(nameof(format), format.ToString()); } }
protected override bool TryCopyFrom(BitmapContent sourceBitmap, Rectangle sourceRegion, Rectangle destinationRegion) { SurfaceFormat sourceFormat; if (!sourceBitmap.TryGetFormat(out sourceFormat)) { return(false); } SurfaceFormat format; TryGetFormat(out format); // A shortcut for copying the entire bitmap to another bitmap of the same type and format if (format == sourceFormat && (sourceRegion == new Rectangle(0, 0, Width, Height)) && sourceRegion == destinationRegion) { SetPixelData(sourceBitmap.GetPixelData()); return(true); } // Destination region copy is not yet supported if (destinationRegion != new Rectangle(0, 0, Width, Height)) { return(false); } // If the source is not Vector4 or requires resizing, send it through BitmapContent.Copy if (!(sourceBitmap is PixelBitmapContent <Vector4>) || sourceRegion.Width != destinationRegion.Width || sourceRegion.Height != destinationRegion.Height) { try { BitmapContent.Copy(sourceBitmap, sourceRegion, this, destinationRegion); return(true); } catch (InvalidOperationException) { return(false); } } //SquishFlags targetFormat = SquishFlags.ColourClusterFit; Format outputFormat = Format.DXT1; switch (format) { case SurfaceFormat.Dxt1: outputFormat = Format.DXT1; break; case SurfaceFormat.Dxt1SRgb: outputFormat = Format.DXT1; break; case SurfaceFormat.Dxt3: outputFormat = Format.DXT3; break; case SurfaceFormat.Dxt3SRgb: outputFormat = Format.DXT3; break; case SurfaceFormat.Dxt5: outputFormat = Format.DXT5; break; case SurfaceFormat.Dxt5SRgb: outputFormat = Format.DXT5; break; default: return(false); } // libsquish requires RGBA8888 var colorBitmap = new PixelBitmapContent <Color>(sourceBitmap.Width, sourceBitmap.Height); BitmapContent.Copy(sourceBitmap, colorBitmap); var sourceData = colorBitmap.GetPixelData(); /* * var dataSize = Squish.GetStorageRequirements(colorBitmap.Width, colorBitmap.Height, targetFormat); * var data = new byte[dataSize]; * var metric = new float[] { 1.0f, 1.0f, 1.0f }; * Squish.CompressImage(sourceData, colorBitmap.Width, colorBitmap.Height, data, targetFormat, metric); * SetPixelData(data); */ var dxtCompressor = new Compressor(); var inputOptions = new InputOptions(); if (outputFormat != Format.DXT1) { inputOptions.SetAlphaMode(AlphaMode.Premultiplied); } else { inputOptions.SetAlphaMode(AlphaMode.None); } inputOptions.SetTextureLayout(TextureType.Texture2D, colorBitmap.Width, colorBitmap.Height, 1); // Small hack here. NVTT wants 8bit data in BGRA. Flip the B and R channels // again here. GraphicsUtil.BGRAtoRGBA(sourceData); var dataHandle = GCHandle.Alloc(sourceData, GCHandleType.Pinned); try { var dataPtr = dataHandle.AddrOfPinnedObject(); inputOptions.SetMipmapData(dataPtr, colorBitmap.Width, colorBitmap.Height, 1, 0, 0); inputOptions.SetMipmapGeneration(false); inputOptions.SetGamma(1.0f, 1.0f); var outputOptions = new OutputOptions(); outputOptions.SetOutputHeader(false); var handler = new DxtDataHandler(this); outputOptions.SetOutputHandler(handler.BeginImage, handler.WriteData); var compressionOptions = new CompressionOptions(); compressionOptions.SetFormat(outputFormat); compressionOptions.SetQuality(Quality.Normal); dxtCompressor.Compress(inputOptions, compressionOptions, outputOptions); } finally { dataHandle.Free(); } return(true); }
/// <summary> /// Resizes the specified texture and/or generates mipmaps. /// </summary> /// <param name="texture">The texture.</param> /// <param name="width">The desired width.</param> /// <param name="height">The desired height.</param> /// <param name="inputGamma">The input gamma.</param> /// <param name="outputGamma">The output gamma.</param> /// <param name="generateMipmaps"> /// <see langword="true"/> to generate all mipmap levels; otherwise <see langword="false"/>. /// </param> /// <param name="hasAlpha"> /// <see langword="true"/> if <paramref name="texture"/> requires an alpha channel; otherwise, /// <see langword="false"/> if <paramref name="texture"/> is opaque. /// </param> /// <param name="hasFractionalAlpha"> /// <see langword="true"/> if <paramref name="texture"/> contains fractional alpha values; /// otherwise, <see langword="false"/> if <paramref name="texture"/> is opaque or contains only /// binary alpha. /// </param> /// <param name="premultipliedAlpha"> /// <see langword="true"/> when <paramref name="texture"/> is using premultiplied alpha.; /// otherwise, <see langword="false"/>.</param> /// <returns>The resized texture.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="texture"/> is <see langword="null"/>. /// </exception> public static Texture ResizeAndGenerateMipmaps(Texture texture, int width, int height, float inputGamma, float outputGamma, bool generateMipmaps, bool hasAlpha, bool hasFractionalAlpha, bool premultipliedAlpha) { if (texture == null) { throw new ArgumentNullException("texture"); } // NVIDIA Texture Tools expect BGRA 8:8:8:8. if (texture.Description.Format != TextureFormat.B8G8R8A8_UNorm) { throw new ArgumentException("Texture format needs to be B8G8R8A8_UNORM.", "texture"); } if (texture.Description.Dimension != TextureDimension.TextureCube && texture.Description.ArraySize > 1) { throw new NotSupportedException("Resizing and mipmap generation for texture arrays is not supported."); } if (texture.Description.Dimension == TextureDimension.Texture3D) { throw new NotSupportedException("Resizing and mipmap generation for volume textures is not supported."); } // ----- InputOptions var inputOptions = new InputOptions(); inputOptions.SetAlphaMode(hasAlpha ? (premultipliedAlpha ? AlphaMode.Premultiplied : AlphaMode.Transparency) : AlphaMode.None); inputOptions.SetFormat(InputFormat.BGRA_8UB); inputOptions.SetGamma(inputGamma, outputGamma); inputOptions.SetMipmapFilter(MipmapFilter.Box); inputOptions.SetMipmapGeneration(generateMipmaps); bool roundToPowerOfTwo = (width != texture.Description.Width || height != texture.Description.Height); inputOptions.SetRoundMode(roundToPowerOfTwo ? RoundMode.ToNextPowerOfTwo : RoundMode.None); inputOptions.SetWrapMode(WrapMode.Mirror); var description = texture.Description; bool isCube = description.Dimension == TextureDimension.TextureCube; var textureType = isCube ? TextureType.TextureCube : TextureType.Texture2D; inputOptions.SetTextureLayout(textureType, description.Width, description.Height, 1); for (int arrayIndex = 0; arrayIndex < description.ArraySize; arrayIndex++) { for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { int index = texture.GetImageIndex(mipIndex, arrayIndex, 0); var image = texture.Images[index]; var handle = GCHandle.Alloc(image.Data, GCHandleType.Pinned); inputOptions.SetMipmapData(handle.AddrOfPinnedObject(), image.Width, image.Height, 1, arrayIndex, mipIndex); handle.Free(); } } // ----- OutputOptions var outputOptions = new OutputOptions(); outputOptions.SetOutputHeader(false); outputOptions.Error += OnError; description.Format = TextureFormat.R8G8B8A8_UNorm; description.Width = width; description.Height = height; description.MipLevels = generateMipmaps ? CalculateMipLevels(width, height) : 1; var resizedTexture = new Texture(description); var outputHandler = new OutputHandler(resizedTexture); outputOptions.SetOutputHandler(outputHandler.BeginImage, outputHandler.WriteData); // ----- CompressionOptions var compressionOptions = new CompressionOptions(); compressionOptions.SetFormat(Format.RGBA); compressionOptions.SetPixelFormat(32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000); compressionOptions.SetQuality(Quality.Normal); // ----- Run NVTT try { var compressor = new Compressor(); compressor.Compress(inputOptions, compressionOptions, outputOptions); } catch (NullReferenceException) { // Resizing and mipmap generation without compression sometimes causes a // NullReferenceException in nvttCompress(). throw new Exception("NullReferenceException in NVIDIA texture tools. Please try again."); } return(resizedTexture); }
/// <summary> /// Compresses the specified texture using a Block Compression format (BC<i>n</i>). /// </summary> /// <param name="texture">The uncompressed texture.</param> /// <param name="inputGamma">The input gamma.</param> /// <param name="outputGamma">The output gamma.</param> /// <param name="generateMipmaps"> /// <see langword="true"/> to generate all mipmap levels; otherwise <see langword="false"/>. /// </param> /// <param name="hasAlpha"> /// <see langword="true"/> if <paramref name="texture"/> requires an alpha channel; otherwise, /// <see langword="false"/> if <paramref name="texture"/> is opaque. /// </param> /// <param name="hasFractionalAlpha"> /// <see langword="true"/> if <paramref name="texture"/> contains fractional alpha values; /// otherwise, <see langword="false"/> if <paramref name="texture"/> is opaque or contains only /// binary alpha. /// </param> /// <param name="premultipliedAlpha"> /// <see langword="true"/> when <paramref name="texture"/> is using premultiplied alpha.; /// otherwise, <see langword="false"/>.</param> /// <param name="sharpAlpha"> /// <see langword="true"/> when the texture contains a sharp alpha mask; otherwise /// <see langword="false"/>. /// </param> /// <returns>The compressed texture.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="texture"/> is <see langword="null"/>. /// </exception> /// <exception cref="ArgumentException"> /// Texture width and height need to be equal (square texture) and a power of two (POT /// texture). /// </exception> internal static Texture CompressBCn(Texture texture, float inputGamma, float outputGamma, bool generateMipmaps, bool hasAlpha, bool hasFractionalAlpha, bool premultipliedAlpha, bool sharpAlpha = false) { if (texture == null) { throw new ArgumentNullException("texture"); } if (texture.Description.Dimension == TextureDimension.Texture3D) { throw new NotSupportedException("Texture compression for volume textures is not supported."); } // NVIDIA Texture Tools expect BGRA 8:8:8:8. texture = texture.ConvertTo(TextureFormat.B8G8R8A8_UNorm); // ----- InputOptions var inputOptions = new InputOptions(); inputOptions.SetAlphaMode(hasAlpha ? (premultipliedAlpha ? AlphaMode.Premultiplied : AlphaMode.Transparency) : AlphaMode.None); inputOptions.SetFormat(InputFormat.BGRA_8UB); inputOptions.SetGamma(inputGamma, outputGamma); inputOptions.SetMipmapFilter(MipmapFilter.Box); inputOptions.SetMipmapGeneration(generateMipmaps); inputOptions.SetRoundMode(RoundMode.None); // Size is set explicitly. inputOptions.SetWrapMode(WrapMode.Mirror); var description = texture.Description; bool isCube = description.Dimension == TextureDimension.TextureCube; var textureType = isCube ? TextureType.TextureCube : TextureType.Texture2D; inputOptions.SetTextureLayout(textureType, description.Width, description.Height, 1); for (int arrayIndex = 0; arrayIndex < description.ArraySize; arrayIndex++) { for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { int index = texture.GetImageIndex(mipIndex, arrayIndex, 0); var image = texture.Images[index]; var handle = GCHandle.Alloc(image.Data, GCHandleType.Pinned); inputOptions.SetMipmapData(handle.AddrOfPinnedObject(), image.Width, image.Height, 1, arrayIndex, mipIndex); handle.Free(); } } // ----- OutputOptions var outputOptions = new OutputOptions(); outputOptions.SetOutputHeader(false); outputOptions.Error += OnError; Format compressedFormat; if (hasAlpha) { if (sharpAlpha) { compressedFormat = Format.BC2; description.Format = TextureFormat.BC2_UNorm; } else { compressedFormat = Format.BC3; description.Format = TextureFormat.BC3_UNorm; } } else { compressedFormat = Format.BC1; description.Format = TextureFormat.BC1_UNorm; } var compressedTexture = new Texture(description); var outputHandler = new OutputHandler(compressedTexture); outputOptions.SetOutputHandler(outputHandler.BeginImage, outputHandler.WriteData); // ----- CompressionOptions var compressionOptions = new CompressionOptions(); compressionOptions.SetFormat(compressedFormat); compressionOptions.SetQuality(Quality.Normal); // ----- Run NVTT try { var compressor = new Compressor(); compressor.Compress(inputOptions, compressionOptions, outputOptions); } catch (NullReferenceException) { throw new Exception("NullReferenceException in NVIDIA texture tools. Please try again."); } return(compressedTexture); }
private static void CompressDxt(GraphicsProfile profile, TextureContent content, bool generateMipmaps, bool premultipliedAlpha, bool sharpAlpha) { var texData = content.Faces[0][0]; if (profile == GraphicsProfile.Reach) { if (!IsPowerOfTwo(texData.Width) || !IsPowerOfTwo(texData.Height)) { throw new PipelineException("DXT Compressed textures width and height must be powers of two in GraphicsProfile.Reach."); } } var pixelData = texData.GetPixelData(); // Test the alpha channel to figure out if we have alpha. var containsAlpha = false; var containsFracAlpha = false; for (var x = 3; x < pixelData.Length; x += 4) { if (pixelData[x] != 0xFF) { containsAlpha = true; if (pixelData[x] != 0x0) { containsFracAlpha = true; } } } var _dxtCompressor = new Compressor(); var inputOptions = new InputOptions(); if (containsAlpha) { inputOptions.SetAlphaMode(premultipliedAlpha ? AlphaMode.Premultiplied : AlphaMode.Transparency); } else { inputOptions.SetAlphaMode(AlphaMode.None); } inputOptions.SetTextureLayout(TextureType.Texture2D, texData.Width, texData.Height, 1); // Small hack here. NVTT wants 8bit data in BGRA. Flip the B and R channels // again here. GraphicsUtil.BGRAtoRGBA(pixelData); var dataHandle = GCHandle.Alloc(pixelData, GCHandleType.Pinned); var dataPtr = dataHandle.AddrOfPinnedObject(); inputOptions.SetMipmapData(dataPtr, texData.Width, texData.Height, 1, 0, 0); inputOptions.SetMipmapGeneration(generateMipmaps); inputOptions.SetGamma(1.0f, 1.0f); var outputOptions = new OutputOptions(); outputOptions.SetOutputHeader(false); var outputFormat = Format.DXT1; if (containsFracAlpha) { if (sharpAlpha) { outputFormat = Format.DXT3; } else { outputFormat = Format.DXT5; } } var handler = new DxtDataHandler(content, outputFormat); outputOptions.SetOutputHandler(handler.BeginImage, handler.WriteData); var compressionOptions = new CompressionOptions(); compressionOptions.SetFormat(outputFormat); compressionOptions.SetQuality(Quality.Normal); _dxtCompressor.Compress(inputOptions, compressionOptions, outputOptions); dataHandle.Free(); }