/// <summary> /// Generates a full set of mipmaps for the texture. /// </summary> /// <param name="overwriteExistingMipmaps">true if the existing mipmap set is replaced with the new set; false otherwise.</param> public virtual void GenerateMipmaps(bool overwriteExistingMipmaps) { // If we already have mipmaps and we're not supposed to overwrite // them then return without any generation. if (!overwriteExistingMipmaps && faces.Any(f => f.Count > 1)) { return; } // Generate the mips for each face. foreach (var face in faces) { // Remove any existing mipmaps. var faceBitmap = face[0]; face.Clear(); face.Add(faceBitmap); var faceType = faceBitmap.GetType(); int width = faceBitmap.Width; int height = faceBitmap.Height; while (width > 1 && height > 1) { width /= 2; height /= 2; var mip = (BitmapContent)Activator.CreateInstance(faceType, new object[] { width, height }); BitmapContent.Copy(faceBitmap, mip); face.Add(mip); } } }
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); } }
protected override bool TryCopyFrom(BitmapContent sourceBitmap, Rectangle sourceRegion, Rectangle destinationRegion) { SurfaceFormat sourceFormat; if (!sourceBitmap.TryGetFormat(out sourceFormat)) { return(false); } // A shortcut for copying the entire bitmap to another bitmap of the same type and format if (SurfaceFormat.RgbEtc1 == 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); } } // Create the texture object in the PVR library var sourceData = sourceBitmap.GetPixelData(); var rgba32F = (PixelFormat)0x2020202061626772; // static const PixelType PVRStandard32PixelType = PixelType('r', 'g', 'b', 'a', 32, 32, 32, 32); using (var pvrTexture = PVRTexture.CreateTexture(sourceData, (uint)sourceBitmap.Width, (uint)sourceBitmap.Height, 1, rgba32F, true, VariableType.Float, ColourSpace.lRGB)) { // Resize the bitmap if needed if ((sourceBitmap.Width != Width) || (sourceBitmap.Height != Height)) { pvrTexture.Resize((uint)Width, (uint)Height, 1, ResizeMode.Cubic); } pvrTexture.Transcode(PixelFormat.ETC1, VariableType.UnsignedByte, ColourSpace.lRGB /*, CompressorQuality.ETCMediumPerceptual, true*/); var texDataSize = pvrTexture.GetTextureDataSize(0); var texData = new byte[texDataSize]; pvrTexture.GetTextureData(texData, texDataSize); SetPixelData(texData); } return(true); }
// Once arranging is complete, copies each glyph to its chosen position in the single larger output bitmap. static BitmapContent CopyGlyphsToOutput(List <ArrangedGlyph> glyphs, int width, int height) { var output = new PixelBitmapContent <Color>(width, height); foreach (var glyph in glyphs) { var sourceGlyph = glyph.Source; var sourceRegion = sourceGlyph.Subrect; var destinationRegion = new Rectangle(glyph.X + 1, glyph.Y + 1, sourceRegion.Width, sourceRegion.Height); BitmapContent.Copy(sourceGlyph.Bitmap, sourceRegion, output, destinationRegion); sourceGlyph.Bitmap = output; sourceGlyph.Subrect = destinationRegion; } return(output); }
/// <summary> /// Converts all bitmaps for this texture to a different format. /// </summary> /// <param name="newBitmapType">Type being converted to. The new type must be a subclass of BitmapContent, such as PixelBitmapContent or DxtBitmapContent.</param> public void ConvertBitmapType(Type newBitmapType) { if (newBitmapType == null) { throw new ArgumentNullException("newBitmapType"); } if (!newBitmapType.IsSubclassOf(typeof(BitmapContent))) { throw new ArgumentException(string.Format("Type '{0}' is not a subclass of BitmapContent.", newBitmapType)); } if (newBitmapType.IsAbstract) { throw new ArgumentException(string.Format("Type '{0}' is abstract and cannot be allocated.", newBitmapType)); } if (newBitmapType.ContainsGenericParameters) { throw new ArgumentException(string.Format("Type '{0}' contains generic parameters and cannot be allocated.", newBitmapType)); } if (newBitmapType.GetConstructor(new Type[2] { typeof(int), typeof(int) }) == null) { throw new ArgumentException(string.Format("Type '{0} does not have a constructor with signature (int, int) and cannot be allocated.", newBitmapType)); } foreach (var mipChain in faces) { for (var i = 0; i < mipChain.Count; i++) { var src = mipChain[i]; if (src.GetType() != newBitmapType) { var dst = (BitmapContent)Activator.CreateInstance(newBitmapType, new object[] { src.Width, src.Height }); BitmapContent.Copy(src, dst); mipChain[i] = dst; } } } }
internal static BitmapContent Resize(this BitmapContent bitmap, int newWidth, int newHeight) { BitmapContent src = bitmap; SurfaceFormat format; src.TryGetFormat(out format); if (format != SurfaceFormat.Vector4) { var v4 = new PixelBitmapContent <Vector4>(src.Width, src.Height); BitmapContent.Copy(src, v4); src = v4; } // Convert to FreeImage bitmap var bytes = src.GetPixelData(); var fi = FreeImage.ConvertFromRawBits(bytes, FREE_IMAGE_TYPE.FIT_RGBAF, src.Width, src.Height, SurfaceFormat.Vector4.GetSize() * src.Width, 128, 0, 0, 0, true); // Resize var newfi = FreeImage.Rescale(fi, newWidth, newHeight, FREE_IMAGE_FILTER.FILTER_BICUBIC); FreeImage.UnloadEx(ref fi); // Convert back to PixelBitmapContent<Vector4> src = new PixelBitmapContent <Vector4>(newWidth, newHeight); bytes = new byte[SurfaceFormat.Vector4.GetSize() * newWidth * newHeight]; FreeImage.ConvertToRawBits(bytes, newfi, SurfaceFormat.Vector4.GetSize() * newWidth, 128, 0, 0, 0, true); src.SetPixelData(bytes); FreeImage.UnloadEx(ref newfi); // Convert back to source type if required if (format != SurfaceFormat.Vector4) { var s = (BitmapContent)Activator.CreateInstance(bitmap.GetType(), new object[] { newWidth, newHeight }); BitmapContent.Copy(src, s); src = s; } return(src); }
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); } // 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 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); } } // NVTT wants 8bit data in BGRA format. var colorBitmap = new PixelBitmapContent <Color>(sourceBitmap.Width, sourceBitmap.Height); BitmapContent.Copy(sourceBitmap, colorBitmap); var sourceData = colorBitmap.GetPixelData(); var dataHandle = GCHandle.Alloc(sourceData, GCHandleType.Pinned); AlphaMode alphaMode; Format outputFormat; var alphaDither = false; switch (format) { case SurfaceFormat.Dxt1: case SurfaceFormat.Dxt1SRgb: { bool hasTransparency; PrepareNVTT_DXT1(sourceData, out 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. 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); }
protected override bool TryCopyTo(BitmapContent destinationBitmap, Rectangle sourceRegion, Rectangle destinationRegion) { SurfaceFormat destinationFormat; if (!destinationBitmap.TryGetFormat(out destinationFormat)) { return(false); } // A shortcut for copying the entire bitmap to another bitmap of the same type and format if (_format == destinationFormat && (sourceRegion == new Rectangle(0, 0, Width, Height)) && sourceRegion == destinationRegion) { destinationBitmap.SetPixelData(GetPixelData()); return(true); } // If the destination is not Vector4 or requires resizing, send it through BitmapContent.Copy if (!(destinationBitmap is PixelBitmapContent <Vector4>) || sourceRegion.Width != destinationRegion.Width || sourceRegion.Height != destinationRegion.Height) { try { BitmapContent.Copy(this, sourceRegion, destinationBitmap, destinationRegion); return(true); } catch (InvalidOperationException) { return(false); } } // Convert to a Vector4 format var dest = destinationBitmap as PixelBitmapContent <Vector4>; if (default(T) is IPackedVector) { Parallel.For(0, sourceRegion.Height, (y) => { var row = GetRow(sourceRegion.Top + y); for (int x = 0; x < sourceRegion.Width; ++x) { dest.SetPixel(destinationRegion.Left + x, destinationRegion.Top + y, ((IPackedVector)row[sourceRegion.Left + x]).ToVector4()); } }); } else { var converter = new Vector4Converter() as IVector4Converter <T>; // If no converter could be created, converting from this format is not supported if (converter == null) { return(false); } Parallel.For(0, sourceRegion.Height, (y) => { var row = GetRow(sourceRegion.Top + y); for (int x = 0; x < sourceRegion.Width; ++x) { dest.SetPixel(destinationRegion.Left + x, destinationRegion.Top + y, converter.ToVector4(row[sourceRegion.Left + x])); } }); } return(true); }
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); } } // Convert to full colour 32-bit format. Floating point would be preferred for processing, but it appears the ATICompressor does not support this var colorBitmap = new PixelBitmapContent <Color>(sourceRegion.Width, sourceRegion.Height); BitmapContent.Copy(sourceBitmap, sourceRegion, colorBitmap, new Rectangle(0, 0, colorBitmap.Width, colorBitmap.Height)); sourceBitmap = colorBitmap; ATICompressor.CompressionFormat targetFormat; switch (format) { case SurfaceFormat.RgbaAtcExplicitAlpha: targetFormat = ATICompressor.CompressionFormat.AtcRgbaExplicitAlpha; break; case SurfaceFormat.RgbaAtcInterpolatedAlpha: targetFormat = ATICompressor.CompressionFormat.AtcRgbaInterpolatedAlpha; break; default: return(false); } var sourceData = sourceBitmap.GetPixelData(); var compressedData = ATICompressor.Compress(sourceData, Width, Height, targetFormat); SetPixelData(compressedData); return(true); }
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); }