protected override void PlatformCompressTexture( ContentProcessorContext context, TextureContent content, TextureProcessorOutputFormat format, bool isSpriteFont) { format = GetTextureFormatForPlatform(format, context.TargetPlatform); // Make sure we're in a floating point format content.ConvertBitmapType(typeof(PixelBitmapContent <RgbaVector>)); switch (format) { case TextureProcessorOutputFormat.AtcCompressed: GraphicsUtil.CompressAti(content, isSpriteFont); break; case TextureProcessorOutputFormat.Color16Bit: GraphicsUtil.CompressColor16Bit(content); break; case TextureProcessorOutputFormat.DxtCompressed: GraphicsUtil.CompressDxt(context, content, isSpriteFont); break; case TextureProcessorOutputFormat.Etc1Compressed: GraphicsUtil.CompressEtc1(context, content, isSpriteFont); break; case TextureProcessorOutputFormat.PvrCompressed: GraphicsUtil.CompressPvrtc(context, content, isSpriteFont); break; } }
public static void CompressDxt(ContentProcessorContext context, TextureContent content, bool isSpriteFont) { var face = content.Faces[0][0]; if (context.TargetProfile == GraphicsProfile.Reach) { if (!IsPowerOfTwo(face.Width) || !IsPowerOfTwo(face.Height)) { throw new PipelineException( "DXT compression requires width and height must " + "be powers of two in Reach graphics profile."); } } // Test the alpha channel to figure out if we have alpha. var alphaRange = CalculateAlphaRange(face); // TODO: This isn't quite right. // // We should be generating DXT1 textures for cutout alpha // as DXT1 supports 1bit alpha and it uses less memory. // // XNA never generated DXT3 for textures... it always picked // between DXT1 for cutouts and DXT5 for fractional alpha. // // DXT3 however can produce better results for high frequency // alpha like a chain link fence where is DXT5 is better for // low frequency alpha like clouds. I don't know how we can // pick the right thing in this case without a hint. if (isSpriteFont) { CompressFontDXT3(content); } else if (alphaRange == AlphaRange.Opaque) { content.ConvertBitmapType(typeof(Dxt1BitmapContent)); } else if (alphaRange == AlphaRange.Cutout) { content.ConvertBitmapType(typeof(Dxt3BitmapContent)); } else { content.ConvertBitmapType(typeof(Dxt5BitmapContent)); } }
public static void CompressColor16Bit(TextureContent content) { var face = content.Faces[0][0]; var alphaRange = CalculateAlphaRange(face); if (alphaRange == AlphaRange.Opaque) { content.ConvertBitmapType(typeof(PixelBitmapContent <Bgr565>)); } else if (alphaRange == AlphaRange.Cutout) { content.ConvertBitmapType(typeof(PixelBitmapContent <Bgra5551>)); } else { content.ConvertBitmapType(typeof(PixelBitmapContent <Bgra4444>)); } }
/// <summary> /// Performs conversion of the texture content to the correct format. /// </summary> /// <param name="context">The processor context.</param> /// <param name="content">The content to be compressed.</param> /// <param name="format">The user requested format for compression.</param> /// <param name="isSpriteFont">If the texture has represents a sprite font, i.e. is greyscale and has sharp black/white contrast.</param> public void ConvertTexture( ContentProcessorContext context, TextureContent content, TextureProcessorOutputFormat format, bool isSpriteFont) { // We do nothing in this case. if (format == TextureProcessorOutputFormat.NoChange) { return; } // If this is color just make sure the format is right and return it. if (format == TextureProcessorOutputFormat.Color) { content.ConvertBitmapType(typeof(PixelBitmapContent <Color>)); return; } // Handle this common compression format. if (format == TextureProcessorOutputFormat.Color16Bit) { GraphicsUtil.CompressColor16Bit(content); return; } try { // All other formats require platform specific choices. PlatformCompressTexture(context, content, format, isSpriteFont); } catch (EntryPointNotFoundException ex) { context.Logger.LogImportantMessage("Could not find the entry point to compress the texture. " + ex.ToString()); throw; } catch (DllNotFoundException ex) { context.Logger.LogImportantMessage("Could not compress texture. Required shared lib is missing. " + ex.ToString()); throw; } catch (Exception ex) { context.Logger.LogImportantMessage("Could not convert texture. " + ex.ToString()); throw; } }
public static void CompressAti(TextureContent content, bool isSpriteFont) { // If sharp alpha is required (for a font texture page), use 16-bit color instead of PVR if (isSpriteFont) { CompressColor16Bit(content); return; } var face = content.Faces[0][0]; var alphaRange = CalculateAlphaRange(face); if (alphaRange == AlphaRange.Full) { content.ConvertBitmapType(typeof(AtcExplicitBitmapContent)); } else { content.ConvertBitmapType(typeof(AtcInterpolatedBitmapContent)); } }
public static void CompressEtc1(ContentProcessorContext context, TextureContent content, bool isSpriteFont) { // If sharp alpha is required (for a font texture page), use 16-bit color instead of PVR if (isSpriteFont) { CompressColor16Bit(content); return; } var face = content.Faces[0][0]; var alphaRange = CalculateAlphaRange(face); // Use BGRA4444 for textures with non-opaque alpha values if (alphaRange != AlphaRange.Opaque) { content.ConvertBitmapType(typeof(PixelBitmapContent <Bgra4444>)); } else { // PVR SGX does not handle non-POT ETC1 textures. // https://code.google.com/p/libgdx/issues/detail?id=1310 // Since we already enforce POT for PVR and DXT in Reach, we will also enforce POT for ETC1 if (!IsPowerOfTwo(face.Width) || !IsPowerOfTwo(face.Height)) { context.Logger.LogWarning( null, content.Identity, "ETC1 compression requires width and height to be powers of two due to " + "hardware restrictions on some devices. Falling back to BGR565."); content.ConvertBitmapType(typeof(PixelBitmapContent <Bgr565>)); } else { content.ConvertBitmapType(typeof(Etc1BitmapContent)); } } }
public static void CompressPvrtc(ContentProcessorContext context, TextureContent content, bool isSpriteFont) { // If sharp alpha is required (for a font texture page), use 16-bit color instead of PVR if (isSpriteFont) { CompressColor16Bit(content); return; } // Calculate number of mip levels int width = content.Faces[0][0].Height; int height = content.Faces[0][0].Width; if (IsPowerOfTwo(width) && IsPowerOfTwo(height) && width == height) { var face = content.Faces[0][0]; var alphaRange = CalculateAlphaRange(face); if (alphaRange == AlphaRange.Opaque) { content.ConvertBitmapType(typeof(PvrtcRgb4BitmapContent)); } else { content.ConvertBitmapType(typeof(PvrtcRgba4BitmapContent)); } } else { context.Logger.LogWarning( null, content.Identity, "PVR compression requires width and height to " + "be powers of two and equal. Falling back to 16-bit color."); CompressColor16Bit(content); } }
protected abstract void PlatformCompressTexture( ContentProcessorContext context, TextureContent content, TextureProcessorOutputFormat format, bool isSpriteFont);
// Compress the greyscale font texture page using a specially-formulated DXT3 mode public static unsafe void CompressFontDXT3(TextureContent content) { if (content.Faces.Count > 1) { throw new PipelineException("Font textures should only have one face"); } Span <Vector4> block = stackalloc Vector4[16]; for (int i = 0; i < content.Faces[0].Count; ++i) { var face = content.Faces[0][i]; var xBlocks = (face.Width + 3) / 4; var yBlocks = (face.Height + 3) / 4; var dxt3Size = xBlocks * yBlocks * 16; byte[] buffer = new byte[dxt3Size]; var facePixels = face.GetPixelData(); fixed(byte *b = facePixels) { var colors = (Vector4 *)b; int w; int h = 0; int x; int y = 0; while (h < (face.Height & ~3)) { w = 0; x = 0; var h0 = h * face.Width; var h1 = h0 + face.Width; var h2 = h1 + face.Width; var h3 = h2 + face.Width; while (w < (face.Width & ~3)) { block[0] = colors[w + h0]; block[1] = colors[w + h0 + 1]; block[2] = colors[w + h0 + 2]; block[3] = colors[w + h0 + 3]; block[4] = colors[w + h1]; block[5] = colors[w + h1 + 1]; block[6] = colors[w + h1 + 2]; block[7] = colors[w + h1 + 3]; block[8] = colors[w + h2]; block[9] = colors[w + h2 + 1]; block[10] = colors[w + h2 + 2]; block[11] = colors[w + h2 + 3]; block[12] = colors[w + h3]; block[13] = colors[w + h3 + 1]; block[14] = colors[w + h3 + 2]; block[15] = colors[w + h3 + 3]; int offset = (x + y * xBlocks) * 16; CompressFontDXT3Block(block, buffer, offset); w += 4; ++x; } // Do partial block at end of row if (w < face.Width) { var cols = face.Width - w; block.Clear(); for (int r = 0; r < 4; ++r) { h0 = (h + r) * face.Width; for (int c = 0; c < cols; ++c) { block[(r * 4) + c] = colors[w + h0 + c]; } } int offset = (x + y * xBlocks) * 16; CompressFontDXT3Block(block, buffer, offset); } h += 4; y++; } // Do last partial row if (h < face.Height) { var rows = face.Height - h; w = 0; x = 0; while (w < (face.Width & ~3)) { block.Clear(); for (int r = 0; r < rows; ++r) { var h0 = (h + r) * face.Width; block[(r * 4) + 0] = colors[w + h0 + 0]; block[(r * 4) + 1] = colors[w + h0 + 1]; block[(r * 4) + 2] = colors[w + h0 + 2]; block[(r * 4) + 3] = colors[w + h0 + 3]; } int offset = (x + y * xBlocks) * 16; CompressFontDXT3Block(block, buffer, offset); w += 4; x++; } // Do last partial block if (w < face.Width) { var cols = face.Width - w; block.Clear(); for (int r = 0; r < rows; ++r) { var h0 = (h + r) * face.Width; for (int c = 0; c < cols; ++c) { block[(r * 4) + c] = colors[w + h0 + c]; } } int offset = (x + y * xBlocks) * 16; CompressFontDXT3Block(block, buffer, offset); } } } var dxt3 = new Dxt3BitmapContent(face.Width, face.Height); dxt3.SetPixelData(buffer); content.Faces[0][i] = dxt3; } }