// 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); }
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); } }
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 }); }