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); } // 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) { 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); } try { Copy(sourceBitmap, sourceRegion, this, destinationRegion); return(true); } catch (InvalidOperationException) { return(false); } }
// Constructor. public Glyph(Rune character, BitmapContent bitmap, Rectangle?subrect = null) { Character = character; Bitmap = bitmap; Subrect = subrect.GetValueOrDefault(new Rectangle(0, 0, bitmap.Width, bitmap.Height)); Width = bitmap.Width; Height = bitmap.Height; }
protected override bool TryCopyFrom(BitmapContent sourceBitmap, Rectangle sourceRegion, Rectangle destinationRegion) { if (!sourceBitmap.TryGetFormat(out SurfaceFormat 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 <RgbaVector> && sourceRegion.Width == destinationRegion.Width && sourceRegion.Height == destinationRegion.Height) { // 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); } try { Copy(sourceBitmap, sourceRegion, this, destinationRegion); return(true); } catch (InvalidOperationException) { return(false); } }
protected override bool TryCopyTo( BitmapContent dstBitmap, Rectangle srcRegion, Rectangle dstRegion) { if (dstBitmap == null) { throw new ArgumentNullException(nameof(dstBitmap)); } if (!dstBitmap.TryGetFormat(out SurfaceFormat destinationFormat)) { return(false); } // A shortcut for copying the entire bitmap to another bitmap of the same type and format if (_format == destinationFormat && srcRegion == new Rectangle(0, 0, Width, Height) && srcRegion == dstRegion) { dstBitmap.SetPixelData(GetPixelData()); return(true); } if (dstBitmap is PixelBitmapContent <RgbaVector> dst && srcRegion.Width == dstRegion.Width && srcRegion.Height == dstRegion.Height) { // TODO: use Image.ConvertPixels // Convert to a RgbaVector format for (int y = 0; y < srcRegion.Height; y++) { var srcRow = GetRowSpan(srcRegion.Top + y); var dstRow = dst.GetRowSpan(dstRegion.Top + y); for (int x = 0; x < srcRegion.Width; x++) { dstRow[dstRegion.Left + x].FromScaledVector( srcRow[srcRegion.Left + x].ToScaledVector4()); } } return(true); } try { Copy(this, srcRegion, dstBitmap, dstRegion); return(true); } catch (InvalidOperationException) { return(false); } }
protected override bool TryCopyFrom( BitmapContent srcBitmap, Rectangle srcRegion, Rectangle dstRegion) { if (srcBitmap == null) { throw new ArgumentNullException(nameof(srcBitmap)); } if (!srcBitmap.TryGetFormat(out SurfaceFormat sourceFormat)) { return(false); } // A shortcut for copying the entire bitmap to another bitmap of the same type and format if (_format == sourceFormat && srcRegion == new Rectangle(0, 0, Width, Height) && srcRegion == dstRegion) { SetPixelData(srcBitmap.GetPixelData()); return(true); } // If the source is RgbaVector and doesn't require resizing, just copy if (srcBitmap is PixelBitmapContent <RgbaVector> src && srcRegion.Width == dstRegion.Width && srcRegion.Height == dstRegion.Height) { // TODO: use Image.ConvertPixels for (int y = 0; y < srcRegion.Height; y++) { var srcRow = src.GetRowSpan(srcRegion.Top + y); var dstRow = GetRowSpan(dstRegion.Top + y); for (int x = 0; x < srcRegion.Width; x++) { dstRow[dstRegion.X + x].FromScaledVector(srcRow[srcRegion.Left + x].ToScaledVector4()); } } return(true); } try { Copy(srcBitmap, srcRegion, this, dstRegion); return(true); } catch (InvalidOperationException) { return(false); } }
/// <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(nameof(newBitmapType)); } if (!newBitmapType.IsSubclassOf(typeof(BitmapContent))) { throw new ArgumentException( $"Type '{newBitmapType}' is not a subclass of BitmapContent."); } if (newBitmapType.IsAbstract) { throw new ArgumentException( $"Type '{newBitmapType}' is abstract and cannot be allocated."); } if (newBitmapType.ContainsGenericParameters) { throw new ArgumentException( $"Type '{newBitmapType}' contains generic parameters and cannot be allocated."); } if (newBitmapType.GetConstructor(new Type[2] { typeof(int), typeof(int) }) == null) { throw new ArgumentException( $"Type '{newBitmapType} does not have a " + $"constructor with signature (int, int) and cannot be allocated."); } 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; } } } }
protected override bool TryCopyTo(BitmapContent destinationBitmap, Rectangle sourceRegion, Rectangle destinationRegion) { if (!destinationBitmap.TryGetFormat(out SurfaceFormat destinationFormat)) { return(false); } TryGetFormat(out SurfaceFormat format); // 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); } // No other support for copying from a PVR texture yet return(false); }
// Once arranging is complete, copies each glyph to its chosen position in the single larger output bitmap. static PixelBitmapContent <Color> 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> /// Checks whether an area of a bitmap contains entirely the specified alpha value. /// </summary> /// <param name="expectedAlpha"></param> /// <param name="bitmap"></param> /// <param name="region"></param> /// <returns></returns> public static bool IsAlphaEntirely(byte expectedAlpha, BitmapContent bitmap, Rectangle?region = null) { var bitmapRegion = region ?? new Rectangle(0, 0, bitmap.Width, bitmap.Height); if (bitmap is PixelBitmapContent <Alpha8> alphaBmp) { for (int y = 0; y < bitmapRegion.Height; y++) { var row = alphaBmp.GetRowSpan(bitmapRegion.Y + y); for (int x = 0; x < bitmapRegion.Width; x++) { if (row[bitmapRegion.X + x].A != expectedAlpha) { return(false); } } } return(true); } else if (bitmap is PixelBitmapContent <Color> rgbaBmp) { for (int y = 0; y < bitmapRegion.Height; y++) { var row = rgbaBmp.GetRowSpan(bitmapRegion.Y + y); for (int x = 0; x < bitmapRegion.Width; x++) { if (row[bitmapRegion.X + x].A != expectedAlpha) { return(false); } } } return(true); } else { throw new ArgumentException( "Expected PixelBitmapContent<Alpha8> or PixelBitmapContent<Rgba32> but got " + bitmap.GetType().Name, nameof(bitmap)); } }
internal static PixelBitmapContent <RgbaVector> Resize( this BitmapContent bitmap, int newWidth, int newHeight) { BitmapContent src = bitmap; src.TryGetFormat(out SurfaceFormat format); if (format != SurfaceFormat.Vector4) { var v4 = new PixelBitmapContent <RgbaVector>(src.Width, src.Height); BitmapContent.Copy(src, v4); src = v4; } using (var image = Image.WrapMemory <RgbaVector>(src.GetPixelData(), new Size(src.Height, src.Width))) using (var resized = image.ProcessRows(x => x.Resize(new Size(newWidth, newHeight), 0))) { var result = new PixelBitmapContent <RgbaVector>(newWidth, newHeight); result.SetPixelData(resized.GetPixelSpan()); return(result); } }
/// <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) { if (width > 1) { width /= 2; } if (height > 1) { height /= 2; } var mip = (BitmapContent)Activator.CreateInstance( faceType, new object[] { width, height }); BitmapContent.Copy(faceBitmap, mip); face.Add(mip); } } }
/// <summary> /// Gets the alpha range in a set of pixels. /// </summary> /// <param name="bitmap">A bitmap of RGBA pixel data.</param> /// <returns>A member of the AlphaRange enum to describe the range of alpha in the pixel data.</returns> static AlphaRange CalculateAlphaRange(BitmapContent bitmap) { var result = AlphaRange.Opaque; if (bitmap is PixelBitmapContent <Color> rgbaBmp) { var pixels = rgbaBmp.GetPixelSpan(); for (int i = 0; i < pixels.Length; ++i) { Color pixel = pixels[i]; if (pixel.A == 0) { result = AlphaRange.Cutout; } else if (pixel.A < byte.MaxValue) { return(AlphaRange.Full); } } } return(result); }
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); } }
// Rasterizes a single character glyph. private Glyph ImportGlyph(Rune character, Face face) { uint glyphIndex = face.GetCharIndex((uint)character.Value); face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal); face.Glyph.RenderGlyph(RenderMode.Normal); // Render the character. BitmapContent glyphBitmap = null; if (face.Glyph.Bitmap.Width > 0 && face.Glyph.Bitmap.Rows > 0) { glyphBitmap = new PixelBitmapContent <Alpha8>(face.Glyph.Bitmap.Width, face.Glyph.Bitmap.Rows); byte[] gpixelAlphas = new byte[face.Glyph.Bitmap.Width * face.Glyph.Bitmap.Rows]; //if the character bitmap has 1bpp we have to expand the buffer data to get the 8bpp pixel data //each byte in bitmap.bufferdata contains the value of to 8 pixels in the row //if bitmap is of width 10, each row has 2 bytes with 10 valid bits, and the last 6 bits of 2nd byte must be discarded if (face.Glyph.Bitmap.PixelMode == PixelMode.Mono) { //variables needed for the expansion, amount of written data, length of the data to write int written = 0, length = face.Glyph.Bitmap.Width * face.Glyph.Bitmap.Rows; for (int i = 0; written < length; i++) { //width in pixels of each row int width = face.Glyph.Bitmap.Width; while (width > 0) { //valid data in the current byte int stride = Math.Min(8, width); //copy the valid bytes to pixeldata //System.Array.Copy(ExpandByte(face.Glyph.Bitmap.BufferData[i]), 0, gpixelAlphas, written, stride); ExpandByteAndCopy(face.Glyph.Bitmap.BufferData[i], stride, gpixelAlphas, written); written += stride; width -= stride; if (width > 0) { i++; } } } } else { Marshal.Copy(face.Glyph.Bitmap.Buffer, gpixelAlphas, 0, gpixelAlphas.Length); } glyphBitmap.SetPixelData(gpixelAlphas); } if (glyphBitmap == null) { var gHA = face.Glyph.Metrics.HorizontalAdvance >> 6; var gVA = face.Size.Metrics.Height >> 6; gHA = gHA > 0 ? gHA : gVA; gVA = gVA > 0 ? gVA : gHA; glyphBitmap = new PixelBitmapContent <Alpha8>(gHA, gVA); } // not sure about this at all var abc = new ABCFloat { A = face.Glyph.Metrics.HorizontalBearingX >> 6, B = face.Glyph.Metrics.Width >> 6 }; abc.C = (face.Glyph.Metrics.HorizontalAdvance >> 6) - (abc.A + abc.B); // Construct the output Glyph object. return(new Glyph(character, glyphBitmap) { XOffset = -(face.Glyph.Advance.X >> 6), XAdvance = face.Glyph.Metrics.HorizontalAdvance >> 6, YOffset = -(face.Glyph.Metrics.HorizontalBearingY >> 6), CharacterWidths = abc }); }