internal static void ChangeTextureToRequestedFormat(TextureContent texture, Type originalType, TextureProcessorOutputFormat textureFormat) { switch (textureFormat) { case TextureProcessorOutputFormat.NoChange: if (originalType == null) { break; } texture.ConvertBitmapType(originalType); return; case TextureProcessorOutputFormat.Color: texture.ConvertBitmapType(typeof (PixelBitmapContent<Color>)); return; case TextureProcessorOutputFormat.DxtCompressed: BestGuessCompress(texture); break; default: return; } }
public override TextureContent Process(TextureContent input, ContentProcessorContext context) { input.ConvertBitmapType(typeof(PixelBitmapContent<Color>)); foreach (MipmapChain mipChain in input.Faces) { foreach (PixelBitmapContent<Color> bitmap in mipChain) { for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { Color c = bitmap.GetPixel(x, y); c.R = (byte)(c.R * c.A / 255); c.G = (byte)(c.G * c.A / 255); c.B = (byte)(c.B * c.A / 255); bitmap.SetPixel(x, y, c); } } } } return base.Process(input, context); }
/// <summary> /// Compresses TextureContent in a format appropriate to the platform /// </summary> public static void CompressTexture(GraphicsProfile profile, TextureContent content, TextureProcessorOutputFormat format, ContentProcessorContext context, bool generateMipmaps, bool sharpAlpha) { format = GetTextureFormatForPlatform(format, context.TargetPlatform); // Make sure we're in a floating point format content.ConvertBitmapType(typeof(PixelBitmapContent <Vector4>)); switch (format) { case TextureProcessorOutputFormat.AtcCompressed: CompressAti(content, generateMipmaps); break; case TextureProcessorOutputFormat.Color16Bit: CompressColor16Bit(content, generateMipmaps); break; case TextureProcessorOutputFormat.DxtCompressed: CompressDxt(profile, content, generateMipmaps, sharpAlpha); break; case TextureProcessorOutputFormat.Etc1Compressed: CompressEtc1(content, generateMipmaps); break; case TextureProcessorOutputFormat.PvrCompressed: CompressPvrtc(content, generateMipmaps); break; } }
public static void CompressDxt(GraphicsProfile profile, TextureContent content, bool generateMipMaps, bool isSpriteFont) { var face = content.Faces[0][0]; if (profile == 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); if (isSpriteFont) { CompressFontDXT3(content, generateMipMaps); } else if (alphaRange == AlphaRange.Opaque) { Compress(typeof(Dxt1BitmapContent), content, generateMipMaps); } else if (alphaRange == AlphaRange.Cutout) { Compress(typeof(Dxt3BitmapContent), content, generateMipMaps); } else { Compress(typeof(Dxt5BitmapContent), content, generateMipMaps); } }
private static void CompressPvrtc(TextureContent content, bool generateMipmaps, bool premultipliedAlpha) { // TODO: Once uncompressed mipmap generation is supported, first use NVTT to generate mipmaps, // then compress them withthe PVRTC tool, so we have the same implementation of mipmap generation // across platforms. // Calculate number of mip levels var width = content.Faces[0][0].Height; var height = content.Faces[0][0].Width; var numberOfMipLevels = 1; if (generateMipmaps) { while (height != 1 || width != 1) { height = Math.Max(height / 2, 1); width = Math.Max(width / 2, 1); numberOfMipLevels++; } } IntPtr dataSizesPtr = IntPtr.Zero; var texDataPtr = ManagedPVRTC.ManagedPVRTC.CompressTexture(content.Faces[0][0].GetPixelData(), content.Faces[0][0].Height, content.Faces[0][0].Width, numberOfMipLevels, premultipliedAlpha, true, ref dataSizesPtr); // Store the size of each mip level var dataSizesArray = new int[numberOfMipLevels]; Marshal.Copy(dataSizesPtr, dataSizesArray, 0, dataSizesArray.Length); var levelSize = 0; byte[] levelData; var sourceWidth = content.Faces[0][0].Width; var sourceHeight = content.Faces[0][0].Height; content.Faces[0].Clear(); for (int x = 0; x < numberOfMipLevels; x++) { levelSize = dataSizesArray[x]; levelData = new byte[levelSize]; Marshal.Copy(texDataPtr, levelData, 0, levelSize); var levelWidth = Math.Max(sourceWidth >> x, 1); var levelHeight = Math.Max(sourceHeight >> x, 1); var bmpContent = new PvrtcBitmapContent(4, sourceWidth, sourceHeight); bmpContent.SetPixelData(levelData); content.Faces[0].Add(bmpContent); texDataPtr = IntPtr.Add(texDataPtr, levelSize); } }
private TextureContent GenerateCubemap(TextureContent input, ContentProcessorContext context) { if (input.Faces[1].Count != 0) { //its already a cubemap return base.Process(input, context); } TextureCubeContent cubeContent = new TextureCubeContent(); // Convert the input data to Color format, for ease of processing. input.ConvertBitmapType(typeof(PixelBitmapContent<Color>)); int height = input.Faces[0][0].Height; int width = input.Faces[0][0].Width / 6; //split the image into 6 pieces, setup: X+,X-, Y+,Y-, Z+, Z- cubeContent.Faces[(int)CubeMapFace.PositiveX] = CreateFace(input.Faces[0][0], width, height, 0); cubeContent.Faces[(int)CubeMapFace.NegativeX] = CreateFace(input.Faces[0][0], width, height, width * 1); cubeContent.Faces[(int)CubeMapFace.PositiveY] = CreateFace(input.Faces[0][0], width, height, width * 2); cubeContent.Faces[(int)CubeMapFace.NegativeY] = CreateFace(input.Faces[0][0], width, height, width * 3); cubeContent.Faces[(int)CubeMapFace.PositiveZ] = CreateFace(input.Faces[0][0], width, height, width * 4); cubeContent.Faces[(int)CubeMapFace.NegativeZ] = CreateFace(input.Faces[0][0], width, height, width * 5); // Calculate mipmap data. cubeContent.GenerateMipmaps(true); // Compress the cubemap into DXT1 format. cubeContent.ConvertBitmapType(typeof(Dxt1BitmapContent)); return cubeContent; }
static void Compress(Type targetType, TextureContent content, bool generateMipMaps) { var wh = new object[2]; if (generateMipMaps) { for (int i = 0; i < content.Faces.Count; ++i) { // Only generate mipmaps if there are none already if (content.Faces[i].Count == 1) { var src = content.Faces[i][0]; var w = src.Width; var h = src.Height; content.Faces[i].Clear(); wh[0] = w; wh[1] = h; var dest = (BitmapContent)Activator.CreateInstance(targetType, wh); BitmapContent.Copy(src, dest); content.Faces[i].Add(dest); while (w > 1 && h > 1) { if (w > 1) { w = w >> 1; } if (h > 1) { h = h >> 1; } wh[0] = w; wh[1] = h; dest = (BitmapContent)Activator.CreateInstance(targetType, wh); BitmapContent.Copy(src, dest); content.Faces[i].Add(dest); } } else { // Convert the existing mipmaps var chain = content.Faces[i]; for (int j = 0; j < chain.Count; ++j) { var src = chain[j]; wh[0] = src.Width; wh[1] = src.Height; var dest = (BitmapContent)Activator.CreateInstance(targetType, wh); BitmapContent.Copy(src, dest); chain[j] = dest; } } } } else { // Converts all existing faces and mipmaps content.ConvertBitmapType(targetType); } }
public static void CompressPvrtc(TextureContent content, bool generateMipMaps) { // Calculate number of mip levels var width = content.Faces[0][0].Height; var height = content.Faces[0][0].Width; if (!IsPowerOfTwo(width) || !IsPowerOfTwo(height)) { throw new PipelineException("PVR compression requires width and height must be powers of two."); } if (width != height) { throw new PipelineException("PVR compression requires square textures."); } var face = content.Faces[0][0]; var alphaRange = CalculateAlphaRange(face); if (alphaRange == AlphaRange.Opaque) { Compress(typeof(PvrtcRgb4BitmapContent), content, generateMipMaps); } else { Compress(typeof(PvrtcRgba4BitmapContent), content, generateMipMaps); } }
/// <summary> /// Compresses TextureContent in a format appropriate to the platform /// </summary> public static void CompressTexture(TextureContent content, TargetPlatform platform, bool premultipliedAlpha) { // TODO: At the moment, only DXT compression from windows machine is supported // Add more here as they become available. switch (platform) { case TargetPlatform.Windows: case TargetPlatform.WindowsPhone: case TargetPlatform.WindowsPhone8: case TargetPlatform.WindowsStoreApp: case TargetPlatform.Ouya: case TargetPlatform.Android: case TargetPlatform.Linux: case TargetPlatform.MacOSX: case TargetPlatform.NativeClient: case TargetPlatform.Xbox360: CompressDXT(content); break; case TargetPlatform.iOS: CompressPVRTC(content, premultipliedAlpha); break; default: throw new NotImplementedException(string.Format("Texture Compression it not implemented for {0}", platform)); } }
public override TextureContent Process( TextureContent input, ContentProcessorContext context ) { logger = context.Logger; logger.LogMessage( "sending texture to base TextureProcessor for initial processing" ); var textureContent = base.Process( input, context ); var bmp = (PixelBitmapContent<Color>)textureContent.Faces[0][0]; var destData = bmp.getData(); // process the data if( flattenImage ) { logger.LogMessage( "flattening image" ); destData = TextureUtils.createFlatHeightmap( destData, opaqueColor, transparentColor ); } if( blurType != BlurType.None ) { logger.LogMessage( "blurring image width blurDeviation: {0}", blurDeviation ); if( blurType == BlurType.Color ) destData = TextureUtils.createBlurredTexture( destData, bmp.Width, bmp.Height, (double)blurDeviation ); else destData = TextureUtils.createBlurredGrayscaleTexture( destData, bmp.Width, bmp.Height, (double)blurDeviation ); } logger.LogMessage( "generating normal map with {0}", edgeDetectionFilter ); destData = TextureUtils.createNormalMap( destData, edgeDetectionFilter, bmp.Width, bmp.Height, normalStrength, invertX, invertY ); bmp.setData( destData ); return textureContent; }
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(context, content); return; } // Calculate number of mip levels var width = content.Faces[0][0].Height; var height = content.Faces[0][0].Width; if (!IsPowerOfTwo(width) || !IsPowerOfTwo(height) || (width != height)) { 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(context, content); return; } var face = content.Faces[0][0]; var alphaRange = CalculateAlphaRange(face); if (alphaRange == AlphaRange.Opaque) { content.ConvertBitmapType(typeof(PvrtcRgb4BitmapContent)); } else { content.ConvertBitmapType(typeof(PvrtcRgba4BitmapContent)); } }
/// <summary> /// Compresses TextureContent in a format appropriate to the platform /// </summary> public static void CompressTexture(GraphicsProfile profile, TextureContent content, ContentProcessorContext context, bool generateMipmaps, bool premultipliedAlpha) { // TODO: At the moment, only DXT compression from windows machine is supported // Add more here as they become available. switch (context.TargetPlatform) { case TargetPlatform.Windows: case TargetPlatform.WindowsPhone: case TargetPlatform.WindowsPhone8: case TargetPlatform.WindowsStoreApp: case TargetPlatform.Ouya: case TargetPlatform.Android: case TargetPlatform.Linux: case TargetPlatform.MacOSX: case TargetPlatform.NativeClient: case TargetPlatform.Xbox360: context.Logger.LogMessage("Using DXT Compression"); CompressDxt(profile, content, generateMipmaps); break; case TargetPlatform.iOS: context.Logger.LogMessage("Using PVRTC Compression"); CompressPvrtc(content, generateMipmaps, premultipliedAlpha); break; default: throw new NotImplementedException(string.Format("Texture Compression it not implemented for {0}", context.TargetPlatform)); } }
static public 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(context, 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)); } } }
private static void BestGuessCompress(TextureContent texture) { texture.ConvertBitmapType(typeof (PixelBitmapContent<Color>)); if (!(texture is Texture3DContent)) { texture.ConvertBitmapType(HasFractionalAlpha(texture) ? typeof (Dxt5BitmapContent) : typeof (Dxt1BitmapContent)); } }
public override TextureContent Process(TextureContent input, ContentProcessorContext context) { // Fallback if we aren't buiding for iOS. var platform = ContentHelper.GetMonoGamePlatform(); if (platform != MonoGamePlatform.iOS) return base.Process(input, context); // Only go this path if we are compressing the texture if (TextureFormat != TextureProcessorOutputFormat.DxtCompressed) return base.Process(input, context); // TODO: Reflector ResizeToPowerOfTwo(TextureContent tex) // Resize the first face and let mips get generated from the dll. /*if (ResizeToPowerOfTwo) { }*/ var height = input.Faces[0][0].Height; var width = input.Faces[0][0].Width; var mipLevels = 1; var invalidBounds = height != width || !(isPowerOfTwo(height) && isPowerOfTwo(width)); // Only PVR compress square, power of two textures. if (invalidBounds || compressionMode == MGCompressionMode.NoCompression) { if (compressionMode != MGCompressionMode.NoCompression) { context.Logger.LogImportantMessage("WARNING: PVR Texture {0} must be a square, power of two texture. Skipping Compression.", Path.GetFileName(context.OutputFilename)); } // Skip compressing this texture and process it normally. this.TextureFormat = TextureProcessorOutputFormat.Color; return base.Process(input, context); } // Calculate how many mip levels will be created, and pass that to our DLL. if (GenerateMipmaps) { while (height != 1 || width != 1) { height = Math.Max(height / 2, 1); width = Math.Max(width / 2, 1); mipLevels++; } } ConvertToPVRTC(input, mipLevels, PremultiplyAlpha, compressionMode); return input; }
public DXTDataHandler(TextureContent content, Format format) { _content = content; _currentMipLevel = 0; _levelWidth = content.Faces[0][0].Width; _levelHeight = content.Faces[0][0].Height; _format = format; WriteData = new OutputOptions.WriteDataDelegate(writeData); BeginImage = new OutputOptions.ImageDelegate(beginImage); }
public DxtDataHandler(TextureContent content, Format format) { _content = content; _currentMipLevel = 0; _levelWidth = content.Faces[0][0].Width; _levelHeight = content.Faces[0][0].Height; _format = format; WriteData = new OutputOptions.WriteDataDelegate(writeData); BeginImage = new OutputOptions.ImageDelegate(beginImage); }
static public void CompressAti(TextureContent content, bool generateMipMaps) { var face = content.Faces[0][0]; var alphaRange = CalculateAlphaRange(face); if (alphaRange == AlphaRange.Full) { Compress(typeof(AtcExplicitBitmapContent), content, generateMipMaps); } else { Compress(typeof(AtcInterpolatedBitmapContent), content, generateMipMaps); } }
private static void CompressDxt(GraphicsProfile profile, TextureContent content, bool generateMipmaps) { 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 _dxtCompressor = new Compressor(); var inputOptions = new InputOptions(); inputOptions.SetAlphaMode(AlphaMode.Transparency); inputOptions.SetTextureLayout(TextureType.Texture2D, texData.Width, texData.Height, 1); var pixelData = texData.GetPixelData(); // 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); var containsFracAlpha = ContainsFractionalAlpha(pixelData); var outputOptions = new OutputOptions(); outputOptions.SetOutputHeader(false); var outputFormat = containsFracAlpha ? Format.DXT5 : Format.DXT1; var handler = new DxtDataHandler(content, outputFormat); outputOptions.SetOutputHandler(handler.BeginImage, handler.WriteData); var compressionOptions = new CompressionOptions(); compressionOptions.SetFormat(outputFormat); compressionOptions.SetQuality(Quality.Fastest); _dxtCompressor.Compress(inputOptions, compressionOptions, outputOptions); dataHandle.Free(); }
public override TInput Process(TInput input, ContentProcessorContext context) { PixelBitmapContent <Color> texture = ((PixelBitmapContent <Color>)input.Faces[0][0]); PixelBitmapContent <Color> normalMap = CreateNormalMap(texture); Texture2DContent result = new Texture2DContent(); result.Faces[0] = normalMap; // Call the base with the new texture to gen mipmaps and new format base.Process(result, context); return(result); }
internal static void Resize(this TextureContent content, int newWidth, int newHeight) { var source = content.Faces[0][0].ToSystemBitmap(); var destination = new Bitmap(newWidth, newHeight); using (var graphics = System.Drawing.Graphics.FromImage(destination)) { graphics.DrawImage(source, 0, 0, newWidth, newHeight); source.Dispose(); } content.Faces.Clear(); content.Faces.Add(new MipmapChain(destination.ToXnaBitmap())); }
/// <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="generateMipmaps">If mipmap generation is required.</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 generateMipmaps, 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>)); if (generateMipmaps) { content.GenerateMipmaps(false); } return; } // Handle this common compression format. if (format == TextureProcessorOutputFormat.Color16Bit) { GraphicsUtil.CompressColor16Bit(content, generateMipmaps); return; } try { // All other formats require platform specific choices. PlatformCompressTexture(context, content, format, generateMipmaps, isSpriteFont); } catch (EntryPointNotFoundException ex) { context.Logger.LogImportantMessage("Could not find the entry point to compress the texture. " + ex.ToString()); throw ex; } catch (DllNotFoundException ex) { context.Logger.LogImportantMessage("Could not compress texture. Required shared lib is missing. " + ex.ToString()); throw ex; } catch (Exception ex) { context.Logger.LogImportantMessage("Could not convert texture. " + ex.ToString()); throw ex; } }
private static bool HasFractionalAlpha(TextureContent texture) { foreach (MipmapChain chain in texture.Faces) { foreach (PixelBitmapContent<Color> content in chain) { for (int i = 0; i < content.Height; i++) { if (content.GetRow(i).Select(color => color.A).Any(a => (a != 0xff) && (a != 0))) { return true; } } } } return false; }
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)); } }
static public void CompressColor16Bit(ContentProcessorContext context, 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>)); } }
static public void CompressColor16Bit(TextureContent content, bool generateMipMaps) { var face = content.Faces[0][0]; var alphaRange = CalculateAlphaRange(face); if (alphaRange == AlphaRange.Opaque) { Compress(typeof(PixelBitmapContent <Bgr565>), content, generateMipMaps); } else if (alphaRange == AlphaRange.Cutout) { Compress(typeof(PixelBitmapContent <Bgra5551>), content, generateMipMaps); } else { Compress(typeof(PixelBitmapContent <Bgra4444>), content, generateMipMaps); } }
public static void PremultiplyAlpha(TextureContent content) { var colorTex = content.Faces[0][0] as PixelBitmapContent <Color>; if (colorTex != null) { for (int x = 0; x < colorTex.Height; x++) { var row = colorTex.GetRow(x); for (int y = 0; y < row.Length; y++) { if (row[y].A < 0xff) { row[y] = Color.FromNonPremultiplied(row[y].R, row[y].G, row[y].B, row[y].A); } } } } else { var vec4Tex = content.Faces[0][0] as PixelBitmapContent <Vector4>; if (vec4Tex == null) { throw new NotSupportedException(); } for (int x = 0; x < vec4Tex.Height; x++) { var row = vec4Tex.GetRow(x); for (int y = 0; y < row.Length; y++) { if (row[y].W < 1.0f) { row[y].X *= row[y].W; row[y].Y *= row[y].W; row[y].Z *= row[y].W; } } } } }
internal static void Resize(this TextureContent content, int newWidth, int newHeight) { var resizedBmp = new Bitmap(newWidth, newHeight); using (var graphics = System.Drawing.Graphics.FromImage(resizedBmp)) { graphics.DrawImage(content._bitmap, 0, 0, newWidth, newHeight); content._bitmap.Dispose(); content._bitmap = resizedBmp; } var imageData = content._bitmap.GetData(); var bitmapContent = new PixelBitmapContent <Color>(content._bitmap.Width, content._bitmap.Height); bitmapContent.SetPixelData(imageData); content.Faces.Clear(); content.Faces.Add(new MipmapChain(bitmapContent)); }
internal static void Resize(this TextureContent content, int newWidth, int newHeight) { // TODO: This should be refactored to use FreeImage // with a higher quality filter. var destination = new Bitmap(newWidth, newHeight); using (var source = content.Faces[0][0].ToSystemBitmap()) using (var graphics = System.Drawing.Graphics.FromImage(destination)) { var imageAttr = new ImageAttributes(); imageAttr.SetWrapMode(WrapMode.TileFlipXY); var destRect = new System.Drawing.Rectangle(0, 0, newWidth, newHeight); graphics.InterpolationMode = InterpolationMode.HighQualityBilinear; graphics.DrawImage(source, destRect, 0, 0, source.Width, source.Height, GraphicsUnit.Pixel, imageAttr); } content.Faces[0][0] = destination.ToXnaBitmap(false); //we dont want to flip colors twice }
static public void CompressAti(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(context, 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)); } }
static public void CompressEtc1(TextureContent content, bool generateMipMaps) { var face = content.Faces[0][0]; var alphaRange = CalculateAlphaRange(face); // Use BGRA4444 for textures with non-opaque alpha values if (alphaRange != AlphaRange.Opaque) { Compress(typeof(PixelBitmapContent <Bgra4444>), content, generateMipMaps); } 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)) { throw new PipelineException("ETC1 compression require width and height must be powers of two due to hardware restrictions on some devices."); } Compress(typeof(Etc1BitmapContent), content, generateMipMaps); } }
public override TextureContent Process(TextureContent input, ContentProcessorContext context) { if (input == null) { throw new ArgumentNullException("input"); } //check if its a normal map Console.WriteLine("Light pre-pass texture processor: " + context.OutputFilename); if (context.OutputFilename.Contains("normal")) { TextureFormat = TextureProcessorOutputFormat.Color; } else { TextureFormat = TextureProcessorOutputFormat.DxtCompressed; } GenerateMipmaps = true; //try to parse meta data FileInfo fileInfo = new FileInfo(Path.GetDirectoryName(input.Identity.SourceFilename) + "\\" + Path.GetFileNameWithoutExtension(input.Identity.SourceFilename) + ".metadata"); if (fileInfo.Exists) { using (FileStream fileStream = fileInfo.OpenRead()) { StreamReader streamReader = new StreamReader(fileStream); while (!streamReader.EndOfStream) { string line = streamReader.ReadLine(); ParseMetaData(line); } } } if (_isCubemap) { return GenerateCubemap(input, context); } return base.Process(input, context); }
public static void ColorKey(TextureContent texture, Color colorKey) { foreach (MipmapChain chain in texture.Faces) { foreach (BitmapContent content in chain) { var content3 = content as PixelBitmapContent<Color>; if (content3 == null) { var content2 = content as PixelBitmapContent<Vector4>; if (content2 == null) { throw new NotSupportedException(); } content2.ReplaceColor(colorKey.ToVector4(), Vector4.Zero); } else { content3.ReplaceColor(colorKey, Color.Transparent); } } } }
public override TextureContent Process(TextureContent input, ContentProcessorContext context) { input.ConvertBitmapType(typeof(PixelBitmapContent<Color>)); foreach (var mipmapChain in input.Faces) { for (int i = 0; i < mipmapChain.Count; i++) { var bitmap = mipmapChain[i] as PixelBitmapContent<Color>; int newWidth = (int) ((float) bitmap.Width * Scale); int newHeight = (int) ((float) bitmap.Height * Scale); var newBitmap = new PixelBitmapContent<Color>(newWidth, newHeight); for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { for (int scaleY = 0; scaleY < Scale; scaleY++) { for (int scaleX = 0; scaleX < Scale; scaleX++) { newBitmap.SetPixel( (int) (x * Scale) + scaleX, (int) (y * Scale) + scaleY, bitmap.GetPixel(x, y)); } } } } mipmapChain[i] = newBitmap; } } return base.Process(input, context); }
private static void CompressPVRTC(TextureContent content, bool premultipliedAlpha) { // Note: MipGeneration will be done by NVTT, rather than PVRTC's tool. // This way we have the same implementation across platforms. IntPtr dataSizesPtr = IntPtr.Zero; var texDataPtr = compressPVRTC(content.Faces[0][0].GetPixelData(), content.Faces[0][0].Height, content.Faces[0][0].Width, 1, premultipliedAlpha, true, ref dataSizesPtr); // Store the size of each mipLevel var dataSizesArray = new int[1]; Marshal.Copy(dataSizesPtr, dataSizesArray, 0, dataSizesArray.Length); var levelSize = 0; byte[] levelData; var sourceWidth = content.Faces[0][0].Width; var sourceHeight = content.Faces[0][0].Height; content.Faces[0].Clear(); levelSize = dataSizesArray[0]; levelData = new byte[levelSize]; Marshal.Copy(texDataPtr, levelData, 0, levelSize); var bmpContent = new PVRTCBitmapContent(4, sourceWidth, sourceHeight); bmpContent.SetPixelData(levelData); content.Faces[0].Add(bmpContent); }
public static void CompressPvrtc(TextureContent content, bool generateMipMaps, bool isSpriteFont) { // If sharp alpha is required (for a font texture page), use 16-bit color instead of PVR if (isSpriteFont) { CompressColor16Bit(content, generateMipMaps); return; } // Calculate number of mip levels var width = content.Faces[0][0].Height; var height = content.Faces[0][0].Width; if (!IsPowerOfTwo(width) || !IsPowerOfTwo(height)) { throw new PipelineException("PVR compression requires width and height must be powers of two."); } if (width != height) { throw new PipelineException("PVR compression requires square textures."); } var face = content.Faces[0][0]; var alphaRange = CalculateAlphaRange(face); if (alphaRange == AlphaRange.Opaque) { Compress(typeof(PvrtcRgb4BitmapContent), content, generateMipMaps); } else { Compress(typeof(PvrtcRgba4BitmapContent), content, generateMipMaps); } }
private static void CompressDxt(TextureContent content, bool generateMipmaps) { var texData = content.Faces[0][0]; if (!IsPowerOfTwo(texData.Width) || !IsPowerOfTwo(texData.Height)) throw new PipelineException("DXT Compressed textures width and height must be powers of two."); var _dxtCompressor = new Compressor(); var inputOptions = new InputOptions(); inputOptions.SetAlphaMode(AlphaMode.Transparency); inputOptions.SetTextureLayout(TextureType.Texture2D, texData.Width, texData.Height, 1); var pixelData = texData.GetPixelData(); // 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); var containsFracAlpha = ContainsFractionalAlpha(pixelData); var outputOptions = new OutputOptions(); outputOptions.SetOutputHeader(false); var outputFormat = containsFracAlpha ? Format.DXT5 : Format.DXT1; var handler = new DxtDataHandler(content, outputFormat); outputOptions.SetOutputHandler(handler.BeginImage, handler.WriteData); var compressionOptions = new CompressionOptions(); compressionOptions.SetFormat(outputFormat); compressionOptions.SetQuality(Quality.Fastest); _dxtCompressor.Compress(inputOptions, compressionOptions, outputOptions); dataHandle.Free(); }
static void CompressAti(TextureContent content, bool generateMipMaps) { var face = content.Faces[0][0]; var alphaRange = CalculateAlphaRange(face); if (alphaRange == AlphaRange.Full) Compress(typeof(AtcExplicitBitmapContent), content, generateMipMaps); else Compress(typeof(AtcInterpolatedBitmapContent), content, generateMipMaps); }
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 <Vector4>)); switch (format) { case TextureProcessorOutputFormat.AtcCompressed: GraphicsUtil.CompressAti(context, content, isSpriteFont); break; case TextureProcessorOutputFormat.Color16Bit: GraphicsUtil.CompressColor16Bit(context, 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 ConvertToPVRTC(TextureContent sourceContent, int mipLevels, bool premultipliedAlpha, MGCompressionMode bpp) { IntPtr dataSizesPtr = IntPtr.Zero; var texDataPtr = CompressTexture(sourceContent.Faces[0][0].GetPixelData(), sourceContent.Faces[0][0].Height, sourceContent.Faces[0][0].Width, mipLevels, premultipliedAlpha, bpp == MGCompressionMode.PVRTCFourBitsPerPixel, ref dataSizesPtr); // Store the size of each mipLevel var dataSizesArray = new int[mipLevels]; Marshal.Copy(dataSizesPtr, dataSizesArray, 0, dataSizesArray.Length); var levelSize = 0; byte[] levelData; var sourceWidth = sourceContent.Faces[0][0].Width; var sourceHeight = sourceContent.Faces[0][0].Height; // Set the pixel data for each mip level. sourceContent.Faces[0].Clear(); for (int x = 0; x < mipLevels; x++) { levelSize = dataSizesArray[x]; levelData = new byte[levelSize]; Marshal.Copy(texDataPtr, levelData, 0, levelSize); var levelWidth = Math.Max(sourceWidth >> x, 1); var levelHeight = Math.Max(sourceHeight >> x, 1); sourceContent.Faces[0].Add(new MGBitmapContent(levelData, levelWidth, levelHeight, bpp)); texDataPtr = IntPtr.Add(texDataPtr, levelSize); } }
public static void PremultiplyAlpha(TextureContent texture) { foreach (MipmapChain chain in texture.Faces) { foreach (BitmapContent content3 in chain) { var content2 = content3 as PixelBitmapContent<Color>; if (content2 != null) { for (int i = 0; i < content2.Height; i++) { Color[] row = content2.GetRow(i); for (int j = 0; j < row.Length; j++) { Color color = row[j]; if (color.A < 0xff) { row[j] = Color.FromNonPremultiplied(color.R, color.G, color.B, color.A); } } } } else { var content = content3 as PixelBitmapContent<Vector4>; if (content == null) { throw new NotSupportedException(); } for (int k = 0; k < content.Height; k++) { Vector4[] vectorArray = content.GetRow(k); for (int m = 0; m < vectorArray.Length; m++) { Vector4 vector = vectorArray[m]; if (vector.W < 1f) { vector.X *= vector.W; vector.Y *= vector.W; vector.Z *= vector.W; vectorArray[m] = vector; } } } } } } }
public static void ResizeToPowerOfTwo(TextureContent texture) { foreach (MipmapChain chain in texture.Faces) { for (int i = 0; i < chain.Count; i++) { BitmapContent source = chain[i]; int width = RoundUpToPowerOfTwo(source.Width); int height = RoundUpToPowerOfTwo(source.Height); if ((width != source.Width) || (height != source.Height)) { chain[i] = ConvertBitmap(source, source.GetType(), width, height); } } } }
protected abstract void PlatformCompressTexture(ContentProcessorContext context, TextureContent content, TextureProcessorOutputFormat format, bool generateMipmaps, bool isSpriteFont);
private static void CompressPvrtc(TextureContent content, bool generateMipMaps) { // Calculate number of mip levels var width = content.Faces[0][0].Height; var height = content.Faces[0][0].Width; if (!IsPowerOfTwo(width) || !IsPowerOfTwo(height)) throw new PipelineException("PVR compression requires width and height must be powers of two."); if (width != height) throw new PipelineException("PVR compression requires square textures."); var face = content.Faces[0][0]; var alphaRange = CalculateAlphaRange(face); if (alphaRange == AlphaRange.Opaque) Compress(typeof(PvrtcRgb4BitmapContent), content, generateMipMaps); else Compress(typeof(PvrtcRgba4BitmapContent), content, generateMipMaps); }
static void Compress(Type targetType, TextureContent content, bool generateMipMaps) { var wh = new object[2]; if (generateMipMaps) { for (int i = 0; i < content.Faces.Count; ++i) { // Only generate mipmaps if there are none already if (content.Faces[i].Count == 1) { var src = content.Faces[i][0]; var w = src.Width; var h = src.Height; content.Faces[i].Clear(); wh[0] = w; wh[1] = h; var dest = (BitmapContent)Activator.CreateInstance(targetType, wh); BitmapContent.Copy(src, dest); content.Faces[i].Add(dest); while (w > 1 && h > 1) { if (w > 1) w = w >> 1; if (h > 1) h = h >> 1; wh[0] = w; wh[1] = h; dest = (BitmapContent)Activator.CreateInstance(targetType, wh); BitmapContent.Copy(src, dest); content.Faces[i].Add(dest); } } else { // Convert the existing mipmaps var chain = content.Faces[i]; for (int j = 0; j < chain.Count; ++j) { var src = chain[j]; wh[0] = src.Width; wh[1] = src.Height; var dest = (BitmapContent)Activator.CreateInstance(targetType, wh); BitmapContent.Copy(src, dest); chain[j] = dest; } } } } else { // Converts all existing faces and mipmaps content.ConvertBitmapType(targetType); } }
/// <summary> /// Compresses TextureContent in a format appropriate to the platform /// </summary> public static void CompressTexture(GraphicsProfile profile, TextureContent content, TextureProcessorOutputFormat format, ContentProcessorContext context, bool generateMipmaps, bool sharpAlpha) { format = GetTextureFormatForPlatform(format, context.TargetPlatform); // Make sure we're in a floating point format content.ConvertBitmapType(typeof(PixelBitmapContent<Vector4>)); switch (format) { case TextureProcessorOutputFormat.AtcCompressed: CompressAti(content, generateMipmaps); break; case TextureProcessorOutputFormat.Color16Bit: CompressColor16Bit(content, generateMipmaps); break; case TextureProcessorOutputFormat.DxtCompressed: CompressDxt(profile, content, generateMipmaps, sharpAlpha); break; case TextureProcessorOutputFormat.Etc1Compressed: CompressEtc1(content, generateMipmaps); break; case TextureProcessorOutputFormat.PvrCompressed: CompressPvrtc(content, generateMipmaps); break; } }
/// <summary> /// Checks that the face of the texture contains 7 mipmaps and that their sizes decline from 64x64 to 1x1 /// </summary> /// <param name="content">Texture to check</param> /// <param name="faceIndex">Index of the face from the texture</param> private static void CheckDdsFace(TextureContent content, int faceIndex) { Assert.AreEqual(content.Faces[faceIndex].Count, 7); Assert.AreEqual(content.Faces[faceIndex][0].Width, 64); Assert.AreEqual(content.Faces[faceIndex][0].Height, 64); Assert.AreEqual(content.Faces[faceIndex][1].Width, 32); Assert.AreEqual(content.Faces[faceIndex][1].Height, 32); Assert.AreEqual(content.Faces[faceIndex][2].Width, 16); Assert.AreEqual(content.Faces[faceIndex][2].Height, 16); Assert.AreEqual(content.Faces[faceIndex][3].Width, 8); Assert.AreEqual(content.Faces[faceIndex][3].Height, 8); Assert.AreEqual(content.Faces[faceIndex][4].Width, 4); Assert.AreEqual(content.Faces[faceIndex][4].Height, 4); Assert.AreEqual(content.Faces[faceIndex][5].Width, 2); Assert.AreEqual(content.Faces[faceIndex][5].Height, 2); Assert.AreEqual(content.Faces[faceIndex][6].Width, 1); Assert.AreEqual(content.Faces[faceIndex][6].Height, 1); }
static void CompressEtc1(TextureContent content, bool generateMipMaps) { var face = content.Faces[0][0]; var alphaRange = CalculateAlphaRange(face); // Use BGRA4444 for textures with non-opaque alpha values if (alphaRange != AlphaRange.Opaque) Compress(typeof(PixelBitmapContent<Bgra4444>), content, generateMipMaps); 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)) throw new PipelineException("ETC1 compression require width and height must be powers of two due to hardware restrictions on some devices."); Compress(typeof(Etc1BitmapContent), content, generateMipMaps); } }
public override TextureContent Process(TextureContent input, ContentProcessorContext context) { // Fallback if we aren't buiding for iOS. var platform = ContentHelper.GetMonoGamePlatform(); if (platform != MonoGamePlatform.iOS) return base.Process(input, context); // Only go this path if we are compressing the texture if (TextureFormat != TextureProcessorOutputFormat.DxtCompressed) return base.Process(input, context); // TODO: Reflector ResizeToPowerOfTwo(TextureContent tex) // Resize the first face and let mips get generated from the dll. /*if (ResizeToPowerOfTwo) { }*/ var height = input.Faces[0][0].Height; var width = input.Faces[0][0].Width; var mipLevels = 1; var invalidBounds = height != width || !(isPowerOfTwo(height) && isPowerOfTwo(width)); // Only PVR compress square, power of two textures. if (invalidBounds || compressionMode == MGCompressionMode.NoCompression) { if (compressionMode != MGCompressionMode.NoCompression) { context.Logger.LogImportantMessage("WARNING: PVR Texture {0} must be a square, power of two texture. Skipping Compression.", Path.GetFileName(context.OutputFilename)); } // Skip compressing this texture and process it normally. this.TextureFormat = TextureProcessorOutputFormat.Color; return base.Process(input, context); } // Calculate how many mip levels will be created, and pass that to our DLL. if (GenerateMipmaps) { while (height != 1 || width != 1) { height = Math.Max(height / 2, 1); width = Math.Max(width / 2, 1); mipLevels++; } } if (PremultiplyAlpha) { var colorTex = input.Faces[0][0] as PixelBitmapContent<Color>; if (colorTex != null) { for (int x = 0; x < colorTex.Height; x++) { var row = colorTex.GetRow(x); for (int y = 0; y < row.Length; y++) { if (row[y].A < 0xff) row[y] = Color.FromNonPremultiplied(row[y].R, row[y].G, row[y].B, row[y].A); } } } else { var vec4Tex = input.Faces[0][0] as PixelBitmapContent<Vector4>; if (vec4Tex == null) throw new NotSupportedException(); for (int x = 0; x < vec4Tex.Height; x++) { var row = vec4Tex.GetRow(x); for (int y = 0; y < row.Length; y++) { if (row[y].W < 1.0f) { row[y].X *= row[y].W; row[y].Y *= row[y].W; row[y].Z *= row[y].W; } } } } } ConvertToPVRTC(input, mipLevels, PremultiplyAlpha, compressionMode); return input; }
// Compress the greyscale font texture page using a specially-formulated DXT3 mode static public unsafe void CompressFontDXT3(TextureContent content) { if (content.Faces.Count > 1) { throw new PipelineException("Font textures should only have one face"); } var block = new 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; var buffer = new byte[dxt3Size]; var bytes = face.GetPixelData(); fixed(byte *b = bytes) { Vector4 *colors = (Vector4 *)b; int w = 0; int h = 0; int x = 0; 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; Array.Clear(block, 0, 16); 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)) { Array.Clear(block, 0, 16); 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; Array.Clear(block, 0, 16); 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; } }
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(); }
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(); }
public void AddFrame(TimeSpan delayTime, TextureContent content) { _frames.Add(new Frame {DelayTime = delayTime, Content = content}); }
/// <summary> /// Compresses TextureContent in a format appropriate to the platform /// </summary> public static void CompressTexture(TextureContent content, ContentProcessorContext context, bool generateMipmaps, bool premultipliedAlpha) { // TODO: At the moment, only DXT compression from windows machine is supported // Add more here as they become available. switch (context.TargetPlatform) { case TargetPlatform.Windows: case TargetPlatform.WindowsPhone: case TargetPlatform.WindowsPhone8: case TargetPlatform.WindowsStoreApp: case TargetPlatform.Ouya: case TargetPlatform.Android: case TargetPlatform.Linux: case TargetPlatform.MacOSX: case TargetPlatform.NativeClient: case TargetPlatform.Xbox360: context.Logger.LogMessage ("Detected {0} using DXT Compression", context.TargetPlatform); CompressDxt(content, generateMipmaps); break; case TargetPlatform.iOS: context.Logger.LogMessage ("Detected {0} using PVRTC Compression", context.TargetPlatform); CompressPvrtc(content, generateMipmaps, premultipliedAlpha); break; default: throw new NotImplementedException(string.Format("Texture Compression it not implemented for {0}", context.TargetPlatform)); } }
private static void CompressDxt(GraphicsProfile profile, TextureContent content, bool generateMipMaps, bool sharpAlpha) { var face = content.Faces[0][0]; if (profile == 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); if (alphaRange == AlphaRange.Opaque) Compress(typeof(Dxt1BitmapContent), content, generateMipMaps); else if (alphaRange == AlphaRange.Cutout || sharpAlpha) Compress(typeof(Dxt3BitmapContent), content, generateMipMaps); else Compress(typeof(Dxt5BitmapContent), content, generateMipMaps); }
private static void CompressPvrtc(TextureContent content, bool generateMipmaps, bool premultipliedAlpha) { // TODO: Once uncompressed mipmap generation is supported, first use NVTT to generate mipmaps, // then compress them withthe PVRTC tool, so we have the same implementation of mipmap generation // across platforms. // Calculate number of mip levels var width = content.Faces[0][0].Height; var height = content.Faces[0][0].Width; if (!IsPowerOfTwo(width) || !IsPowerOfTwo(height)) throw new PipelineException("PVRTC Compressed textures width and height must be powers of two."); if (width != height) throw new PipelineException("PVRTC Compressed textures must be square. i.e width == height."); var numberOfMipLevels = 1; if (generateMipmaps) { while (height != 1 || width != 1) { height = Math.Max(height / 2, 1); width = Math.Max(width / 2, 1); numberOfMipLevels++; } } IntPtr dataSizesPtr = IntPtr.Zero; var texDataPtr = ManagedPVRTC.ManagedPVRTC.CompressTexture(content.Faces[0][0].GetPixelData(), content.Faces[0][0].Height, content.Faces[0][0].Width, numberOfMipLevels, premultipliedAlpha, true, ref dataSizesPtr); // Store the size of each mip level var dataSizesArray = new int[numberOfMipLevels]; Marshal.Copy(dataSizesPtr, dataSizesArray, 0, dataSizesArray.Length); var levelSize = 0; byte[] levelData; var sourceWidth = content.Faces[0][0].Width; var sourceHeight = content.Faces[0][0].Height; content.Faces[0].Clear(); for (int x = 0; x < numberOfMipLevels; x++) { levelSize = dataSizesArray[x]; levelData = new byte[levelSize]; Marshal.Copy(texDataPtr, levelData, 0, levelSize); var levelWidth = Math.Max(sourceWidth >> x, 1); var levelHeight = Math.Max(sourceHeight >> x, 1); var bmpContent = new PvrtcBitmapContent(4, sourceWidth, sourceHeight); bmpContent.SetPixelData(levelData); content.Faces[0].Add(bmpContent); texDataPtr = IntPtr.Add(texDataPtr, levelSize); } }
static void CompressColor16Bit(TextureContent content, bool generateMipMaps) { var face = content.Faces[0][0]; var alphaRange = CalculateAlphaRange(face); if (alphaRange == AlphaRange.Opaque) Compress(typeof(PixelBitmapContent<Bgr565>), content, generateMipMaps); else if (alphaRange == AlphaRange.Cutout) Compress(typeof(PixelBitmapContent<Bgra5551>), content, generateMipMaps); else Compress(typeof(PixelBitmapContent<Bgra4444>), content, generateMipMaps); }
/// <summary> /// Constructs a new skinning data object. /// </summary> public InstancedSkinningDataContent(IDictionary<string, InstancedAnimationClip> animationClips, TextureContent animationTexture) { this.animations = animationClips; this.texture = animationTexture; }
//-------------------------------------------------------------- /// <summary> /// Converts an XNA <see cref="TextureContent"/> to a DigitalRune <see cref="Texture"/>. /// </summary> /// <param name="textureContent">The <see cref="TextureContent"/>.</param> /// <returns>The <see cref="Texture"/>.</returns> public static Texture ToTexture(TextureContent textureContent) { SurfaceFormat surfaceFormat; var bitmapContent0 = textureContent.Faces[0][0]; if (!bitmapContent0.TryGetFormat(out surfaceFormat)) throw new InvalidContentException("Invalid surface format.", textureContent.Identity); var texture2DContent = textureContent as Texture2DContent; if (texture2DContent != null) { var description = new TextureDescription { Dimension = TextureDimension.Texture2D, Width = bitmapContent0.Width, Height = bitmapContent0.Height, Depth = 1, MipLevels = texture2DContent.Mipmaps.Count, ArraySize = 1, Format = surfaceFormat.ToDataFormat() }; var texture = new Texture(description); for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { var bitmapContent = texture2DContent.Mipmaps[mipIndex]; var image = texture.Images[texture.GetImageIndex(mipIndex, 0, 0)]; Buffer.BlockCopy(bitmapContent.GetPixelData(), 0, image.Data, 0, image.Data.Length); } return texture; } var textureCubeContent = textureContent as TextureCubeContent; if (textureCubeContent != null) { var description = new TextureDescription { Dimension = TextureDimension.TextureCube, Width = bitmapContent0.Width, Height = bitmapContent0.Height, Depth = 1, MipLevels = textureCubeContent.Faces[0].Count, ArraySize = 6, Format = surfaceFormat.ToDataFormat() }; var texture = new Texture(description); for (int faceIndex = 0; faceIndex < 6; faceIndex++) { for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { var bitmapContent = textureCubeContent.Faces[faceIndex][mipIndex]; var image = texture.Images[texture.GetImageIndex(mipIndex, faceIndex, 0)]; Buffer.BlockCopy(bitmapContent.GetPixelData(), 0, image.Data, 0, image.Data.Length); } } return texture; } var texture3DContent = textureContent as Texture3DContent; if (texture3DContent != null) { var description = new TextureDescription { Dimension = TextureDimension.Texture3D, Width = bitmapContent0.Width, Height = bitmapContent0.Height, Depth = texture3DContent.Faces.Count, MipLevels = texture3DContent.Faces[0].Count, ArraySize = 1, Format = surfaceFormat.ToDataFormat() }; var texture = new Texture(description); for (int zIndex = 0; zIndex < description.Depth; zIndex++) { for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++) { var bitmapContent = texture3DContent.Faces[zIndex][mipIndex]; var image = texture.Images[texture.GetImageIndex(mipIndex, 0, zIndex)]; Buffer.BlockCopy(bitmapContent.GetPixelData(), 0, image.Data, 0, image.Data.Length); } } return texture; } throw new InvalidOperationException("Invalid texture dimension."); }