/// <summary> /// Processes a texture. /// </summary> /// <param name="input">The texture content to process.</param> /// <param name="context">Context for the specified processor.</param> /// <returns>The converted texture content.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="input"/> or <paramref name="context"/> is <see langword="null"/>. /// </exception> public override TextureContent Process(TextureContent input, ContentProcessorContext context) { if (input == null) throw new ArgumentNullException("input"); if (context == null) throw new ArgumentNullException("context"); // Linear vs. sRGB: // XNA does not support _SRGB texture formats. Texture processing is designed // for non-sRGB formats. (sRGB formats require a different order of operations! // See section "Alpha Blending" in DigitalRune KB.) try { var mipmapChain = input.Faces[0]; // Mipmap chain. var level0 = mipmapChain[0]; // Most detailed mipmap level. int width = level0.Width; int height = level0.Height; // Early out? if (!ColorKeyEnabled && (!GenerateMipmaps || (width == 1 && height == 1) // Does not need mipmaps || mipmapChain.Count > 1) // or already has mipmaps. && !PremultiplyAlpha && (!ResizeToPowerOfTwo || (MathHelper.IsPowerOf2(width) && MathHelper.IsPowerOf2(height))) && !ScaleAlphaToCoverage) { if (Format == DRTextureFormat.NoChange) { // No processing required. return input; } SurfaceFormat surfaceFormat; if (!level0.TryGetFormat(out surfaceFormat)) throw new InvalidContentException("Surface format is not supported.", input.Identity); if ((Format == DRTextureFormat.Color && surfaceFormat == SurfaceFormat.Color) || (Format == DRTextureFormat.Dxt && IsDxt(surfaceFormat))) { // No processing required. return input; } } var texture = TextureHelper.ToTexture(input); var sourceFormat = texture.Description.Format; // Apply color keying. if (ColorKeyEnabled) { // Apply color keying in RGBA 8:8:8:8. texture = texture.ConvertTo(DataFormat.R8G8B8A8_UNORM); TextureHelper.ApplyColorKey(texture, ColorKeyColor.R, ColorKeyColor.G, ColorKeyColor.B, ColorKeyColor.A); } // Normal maps require special treatment (no sRGB, etc.). bool isNormalMap = (Format == DRTextureFormat.Normal || Format == DRTextureFormat.NormalInvertY); if (isNormalMap) { InputGamma = 1.0f; OutputGamma = 1.0f; PremultiplyAlpha = false; } // Check whether alpha channel is used. bool hasAlpha = false; // true if alpha channel != 1. bool hasFractionalAlpha = false; // true if alpha channel has 0 < alpha < 1. if (!isNormalMap) { if (GenerateMipmaps || ResizeToPowerOfTwo || PremultiplyAlpha || Format == DRTextureFormat.Dxt) { try { TextureHelper.HasAlpha(texture, out hasAlpha, out hasFractionalAlpha); } catch (NotSupportedException) { // HasAlpha() does not support the current format. Convert and try again. texture = texture.ConvertTo(DataFormat.R32G32B32A32_FLOAT); TextureHelper.HasAlpha(texture, out hasAlpha, out hasFractionalAlpha); } } } // Convert to high-precision, floating-point format for processing. texture = texture.ConvertTo(DataFormat.R32G32B32A32_FLOAT); if (!isNormalMap) { // Convert texture from gamma space to linear space. TextureHelper.GammaToLinear(texture, InputGamma); } else { // Convert normal map from [0, 1] to [-1, 1]. TextureHelper.UnpackNormals(texture); } // The resize filter needs to consider alpha if the image is not already // premultiplied. PremultiplyAlpha indicates that the source image has // alpha, but is not yet premultiplied. (Premultiplication happen at the // end.) bool alphaTransparency = hasAlpha && PremultiplyAlpha; if (ResizeToPowerOfTwo || context.TargetProfile == GraphicsProfile.Reach && Format == DRTextureFormat.Dxt) { // Resize to power-of-two. int expectedWidth = RoundUpToPowerOfTwo(texture.Description.Width); int expectedHeight = RoundUpToPowerOfTwo(texture.Description.Height); if (expectedWidth != texture.Description.Width || expectedHeight != texture.Description.Height) texture = texture.Resize(expectedWidth, expectedHeight, texture.Description.Depth, ResizeFilter.Kaiser, alphaTransparency, TextureAddressMode.Clamp); } if (Format == DRTextureFormat.Dxt || Format == DRTextureFormat.Normal || Format == DRTextureFormat.NormalInvertY) { // Resize to multiple of four. int expectedWidth = RoundToMultipleOfFour(texture.Description.Width); int expectedHeight = RoundToMultipleOfFour(texture.Description.Height); if (expectedWidth != texture.Description.Width || expectedHeight != texture.Description.Height) texture = texture.Resize(expectedWidth, expectedHeight, texture.Description.Depth, ResizeFilter.Kaiser, alphaTransparency, TextureAddressMode.Clamp); } if (GenerateMipmaps && texture.Description.MipLevels <= 1) { // Generate mipmaps. texture.GenerateMipmaps(ResizeFilter.Box, alphaTransparency, TextureAddressMode.Repeat); } // For debugging: // ColorizeMipmaps(texture); if (!isNormalMap) { if (ScaleAlphaToCoverage) TextureHelper.ScaleAlphaToCoverage(texture, ReferenceAlpha, /* data not yet premultiplied */ false); // Convert texture from linear space to gamma space. TextureHelper.LinearToGamma(texture, OutputGamma); // Premultiply alpha. if (hasAlpha && PremultiplyAlpha) TextureHelper.PremultiplyAlpha(texture); } else { // Renormalize normal map and convert to DXT5nm. TextureHelper.ProcessNormals(texture, Format == DRTextureFormat.NormalInvertY); } // No PVRTC in XNA build. string mgPlatform = ContentHelper.GetMonoGamePlatform(); if (!string.IsNullOrEmpty(mgPlatform) && mgPlatform.ToUpperInvariant() == "IOS") Format = DRTextureFormat.Color; // Convert to from floating-point format to requested output format. switch (Format) { case DRTextureFormat.NoChange: texture = texture.ConvertTo(sourceFormat); input = TextureHelper.ToContent(texture, input.Identity); break; case DRTextureFormat.Color: texture = texture.ConvertTo(DataFormat.R8G8B8A8_UNORM); input = TextureHelper.ToContent(texture, input.Identity); break; case DRTextureFormat.Dxt: if (texture.Description.Dimension == TextureDimension.Texture3D) { texture = texture.ConvertTo(DataFormat.R8G8B8A8_UNORM); input = TextureHelper.ToContent(texture, input.Identity); } else { input = Compress(context, texture, hasAlpha, hasFractionalAlpha, PremultiplyAlpha, input.Identity); #else if (hasFractionalAlpha) texture = texture.ConvertTo(DataFormat.BC3_UNORM); else texture = texture.ConvertTo(DataFormat.BC1_UNORM); input = TextureHelper.ToContent(texture, input.Identity); } break; case DRTextureFormat.Normal: case DRTextureFormat.NormalInvertY: input = Compress(context, texture, true, true, false, input.Identity); #else texture = texture.ConvertTo(DataFormat.BC3_UNORM); input = TextureHelper.ToContent(texture, input.Identity); break; default: throw new NotSupportedException("The specified output format is not supported."); } } catch (Exception ex) { throw new InvalidContentException(ex.Message, input.Identity); } return input; }