/// <summary> /// Retrieves the pixel data that is currently stored in video memory. /// </summary> /// <returns></returns> public Pixmap.Layer RetrievePixelData() { DualityApp.GuardSingleThreadState(); int lastTexId; GL.GetInteger(GetPName.TextureBinding2D, out lastTexId); GL.BindTexture(TextureTarget.Texture2D, this.glTexId); byte[] data = new byte[this.texWidth * this.texHeight * 4]; GL.GetTexImage(TextureTarget.Texture2D, 0, GLPixelFormat.Rgba, PixelType.UnsignedByte, data); GL.BindTexture(TextureTarget.Texture2D, lastTexId); Pixmap.Layer result = new Pixmap.Layer(); result.SetPixelDataRgba(data, this.texWidth, this.texHeight); return result; }
private bool AreImagesEqual(Bitmap referenceImage, Action<Canvas> renderMethod) { Pixmap.Layer image = this.RenderToTexture(referenceImage.Width, referenceImage.Height, renderMethod); Pixmap.Layer reference = new Pixmap.Layer(referenceImage); return this.AreImagesEqual(reference, image); }
private Pixmap.Layer CreateDiffImage(Pixmap.Layer first, Pixmap.Layer second) { if (first == second) return new Pixmap.Layer(first.Width, first.Height); if (first.Width != second.Width) return new Pixmap.Layer(1, 1); if (first.Height != second.Height) return new Pixmap.Layer(1, 1); Pixmap.Layer diff = new Pixmap.Layer(first.Width, first.Height); ColorRgba[] firstData = first.Data; ColorRgba[] secondData = second.Data; ColorRgba[] diffData = diff.Data; for (int i = 0; i < firstData.Length; i++) { diffData[i].R = (byte)MathF.Abs(firstData[i].R - secondData[i].R); diffData[i].G = (byte)MathF.Abs(firstData[i].G - secondData[i].G); diffData[i].B = (byte)MathF.Abs(firstData[i].B - secondData[i].B); diffData[i].A = (byte)MathF.Abs(firstData[i].A - secondData[i].A); } return diff; }
private void TestImagesEqual(Bitmap referenceImage, Action<Canvas> renderMethod) { Pixmap.Layer image = this.RenderToTexture(referenceImage.Width, referenceImage.Height, renderMethod); Pixmap.Layer reference = new Pixmap.Layer(referenceImage); bool equal = this.AreImagesEqual(reference, image); if (!equal && Debugger.IsAttached) { // If the debugger is attached, create a nice diff image for the programmer to view. Pixmap.Layer diff = this.CreateDiffImage(image, reference); } Assert.IsTrue(equal); }
/// <summary> /// Retrieves the rasterized <see cref="System.Drawing.Bitmap"/> for a single glyph. /// </summary> /// <param name="glyph">The glyph of which to retrieve the Bitmap.</param> /// <returns>The Bitmap that has been retrieved, or null if the glyph is not supported.</returns> public Pixmap.Layer GetGlyphBitmap(char glyph) { Rect targetRect; int charIndex = (int)glyph > CharLookup.Length ? 0 : CharLookup[(int)glyph]; this.pixelData.LookupAtlas(charIndex, out targetRect); Pixmap.Layer subImg = new Pixmap.Layer( MathF.RoundToInt(targetRect.W), MathF.RoundToInt(targetRect.H)); this.pixelData.MainLayer.DrawOnto(subImg, BlendMode.Solid, -MathF.RoundToInt(targetRect.X), -MathF.RoundToInt(targetRect.Y)); return subImg; }
private void GenerateResources() { if (this.mat != null || this.texture != null || this.pixelData != null) this.ReleaseResources(); TextRenderingHint textRenderingHint; if (this.renderMode == RenderMode.MonochromeBitmap) textRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit; else textRenderingHint = TextRenderingHint.AntiAliasGridFit; int cols; int rows; cols = rows = (int)Math.Ceiling(Math.Sqrt(SupportedChars.Length)); Pixmap.Layer pixelLayer = new Pixmap.Layer(MathF.RoundToInt(cols * this.internalFont.Size * 1.2f), MathF.RoundToInt(rows * this.internalFont.Height * 1.2f)); Pixmap.Layer glyphTemp; Pixmap.Layer glyphTempTypo; Bitmap bm; Bitmap measureBm = new Bitmap(1, 1); Rect[] atlas = new Rect[SupportedChars.Length]; using (Graphics measureGraphics = Graphics.FromImage(measureBm)) { Brush fntBrush = new SolidBrush(Color.Black); StringFormat formatDef = StringFormat.GenericDefault; formatDef.LineAlignment = StringAlignment.Near; formatDef.FormatFlags = 0; StringFormat formatTypo = StringFormat.GenericTypographic; formatTypo.LineAlignment = StringAlignment.Near; int x = 1; int y = 1; for (int i = 0; i < SupportedChars.Length; ++i) { string str = SupportedChars[i].ToString(CultureInfo.InvariantCulture); bool isSpace = str == " "; SizeF charSize = measureGraphics.MeasureString(str, this.internalFont, pixelLayer.Width, formatDef); // Rasterize a single glyph for rendering bm = new Bitmap((int)Math.Ceiling(Math.Max(1, charSize.Width)), this.internalFont.Height + 1); using (Graphics glyphGraphics = Graphics.FromImage(bm)) { glyphGraphics.Clear(Color.Transparent); glyphGraphics.TextRenderingHint = textRenderingHint; glyphGraphics.DrawString(str, this.internalFont, fntBrush, new RectangleF(0, 0, bm.Width, bm.Height), formatDef); } glyphTemp = new Pixmap.Layer(bm); // Rasterize a single glyph in typographic mode for metric analysis if (!isSpace) { Rectangle glyphTempBounds = glyphTemp.OpaqueBounds(); glyphTemp.SubImage(glyphTempBounds.X, 0, glyphTempBounds.Width, glyphTemp.Height); if (BodyAscentRef.Contains(SupportedChars[i])) this.bodyAscent += glyphTempBounds.Height; bm = new Bitmap((int)Math.Ceiling(Math.Max(1, charSize.Width)), this.internalFont.Height + 1); using (Graphics glyphGraphics = Graphics.FromImage(bm)) { glyphGraphics.Clear(Color.Transparent); glyphGraphics.TextRenderingHint = textRenderingHint; glyphGraphics.DrawString(str, this.internalFont, fntBrush, new RectangleF(0, 0, bm.Width, bm.Height), formatTypo); } glyphTempTypo = new Pixmap.Layer(bm); glyphTempTypo.Crop(true, false); } else { glyphTempTypo = glyphTemp; } // Update xy values if it doesn't fit anymore if (x + glyphTemp.Width + 2 > pixelLayer.Width) { x = 1; y += this.internalFont.Height + MathF.Clamp((int)MathF.Ceiling(this.internalFont.Height * 0.1875f), 3, 10); } // Memorize atlas coordinates & glyph data this.maxGlyphWidth = Math.Max(this.maxGlyphWidth, glyphTemp.Width); this.glyphs[i].width = glyphTemp.Width; this.glyphs[i].height = glyphTemp.Height; this.glyphs[i].offsetX = glyphTemp.Width - glyphTempTypo.Width; if (isSpace) { this.glyphs[i].width /= 2; this.glyphs[i].offsetX /= 2; } atlas[i].X = x; atlas[i].Y = y; atlas[i].W = glyphTemp.Width; atlas[i].H = (this.internalFont.Height + 1); // Draw it onto the font surface glyphTemp.DrawOnto(pixelLayer, BlendMode.Solid, x, y); x += glyphTemp.Width + MathF.Clamp((int)MathF.Ceiling(this.internalFont.Height * 0.125f), 2, 10); } } // White out texture except alpha channel. for (int i = 0; i < pixelLayer.Data.Length; i++) { pixelLayer.Data[i].R = 255; pixelLayer.Data[i].G = 255; pixelLayer.Data[i].B = 255; } // Determine Font properties this.height = this.internalFont.Height; this.ascent = (int)Math.Round(this.internalFont.FontFamily.GetCellAscent(this.internalFont.Style) * this.internalFont.Size / this.internalFont.FontFamily.GetEmHeight(this.internalFont.Style)); this.bodyAscent /= BodyAscentRef.Length; this.descent = (int)Math.Round(this.internalFont.FontFamily.GetCellDescent(this.internalFont.Style) * this.internalFont.GetHeight() / this.internalFont.FontFamily.GetLineSpacing(this.internalFont.Style)); this.baseLine = (int)Math.Round(this.internalFont.FontFamily.GetCellAscent(this.internalFont.Style) * this.internalFont.GetHeight() / this.internalFont.FontFamily.GetLineSpacing(this.internalFont.Style)); // Create internal Pixmap and Texture Resources this.pixelData = new Pixmap(pixelLayer); this.pixelData.Atlas = new List<Rect>(atlas); this.texture = new Texture(this.pixelData, Texture.SizeMode.Enlarge, this.IsPixelGridAligned ? TextureMagFilter.Nearest : TextureMagFilter.Linear, this.IsPixelGridAligned ? TextureMinFilter.Nearest : TextureMinFilter.LinearMipmapLinear); // Select DrawTechnique to use ContentRef<DrawTechnique> technique; if (this.renderMode == RenderMode.MonochromeBitmap) technique = DrawTechnique.Mask; else if (this.renderMode == RenderMode.GrayscaleBitmap) technique = DrawTechnique.Alpha; else if (this.renderMode == RenderMode.SmoothBitmap) technique = DrawTechnique.Alpha; else technique = DrawTechnique.SharpAlpha; // Create and configure internal BatchInfo BatchInfo matInfo = new BatchInfo(technique, ColorRgba.White, this.texture); if (technique == DrawTechnique.SharpAlpha) { matInfo.SetUniform("smoothness", this.size * 3.0f); } this.mat = new Material(matInfo); }
private void GenerateResources() { if (this.mat != null || this.texture != null || this.pixelData != null) this.ReleaseResources(); int cols; int rows; cols = rows = (int)Math.Ceiling(Math.Sqrt(SupportedChars.Length)); Pixmap.Layer pixelLayer = new Pixmap.Layer(MathF.RoundToInt(cols * this.internalFont.Size), MathF.RoundToInt(rows * (this.internalFont.Height + 1))); Pixmap.Layer glyphTemp; Pixmap.Layer glyphTempTypo; Bitmap bm; Bitmap measureBm = new Bitmap(1, 1); Rect[] atlas = new Rect[SupportedChars.Length]; using (Graphics measureGraphics = Graphics.FromImage(measureBm)) { Brush fntBrush = new SolidBrush(Color.Black); StringFormat formatDef = StringFormat.GenericDefault; formatDef.LineAlignment = StringAlignment.Near; formatDef.FormatFlags = 0; StringFormat formatTypo = StringFormat.GenericTypographic; formatTypo.LineAlignment = StringAlignment.Near; int x = 0; int y = 0; for (int i = 0; i < SupportedChars.Length; ++i) { string str = SupportedChars[i].ToString(CultureInfo.InvariantCulture); bool isSpace = str == " "; SizeF charSize = measureGraphics.MeasureString(str, this.internalFont, pixelLayer.Width, formatDef); // Render a single glyph bm = new Bitmap((int)Math.Ceiling(Math.Max(1, charSize.Width)), this.internalFont.Height + 1); using (Graphics glyphGraphics = Graphics.FromImage(bm)) { glyphGraphics.Clear(Color.Transparent); glyphGraphics.TextRenderingHint = (System.Drawing.Text.TextRenderingHint)this.hint; glyphGraphics.DrawString(str, this.internalFont, fntBrush, new RectangleF(0, 0, bm.Width, bm.Height), formatDef); } glyphTemp = new Pixmap.Layer(bm); if (!isSpace) { Rectangle glyphTempBounds = glyphTemp.OpaqueBounds(); glyphTemp.SubImage(glyphTempBounds.X, 0, glyphTempBounds.Width, glyphTemp.Height); if (BodyAscentRef.Contains(SupportedChars[i])) this.bodyAscent += glyphTempBounds.Height; // Render a single glyph in typographic mode bm = new Bitmap((int)Math.Ceiling(Math.Max(1, charSize.Width)), this.internalFont.Height + 1); using (Graphics glyphGraphics = Graphics.FromImage(bm)) { glyphGraphics.Clear(Color.Transparent); glyphGraphics.TextRenderingHint = (System.Drawing.Text.TextRenderingHint)this.hint; glyphGraphics.DrawString(str, this.internalFont, fntBrush, new RectangleF(0, 0, bm.Width, bm.Height), formatTypo); } glyphTempTypo = new Pixmap.Layer(bm); glyphTempTypo.Crop(true, false); } else glyphTempTypo = glyphTemp; // Update xy values if it doesn't fit anymore if (x + glyphTemp.Width + 2 > pixelLayer.Width) { x = 0; y += (this.internalFont.Height + 1) + 2; } // Memorize atlas coordinates & glyph data this.maxGlyphWidth = Math.Max(this.maxGlyphWidth, glyphTemp.Width); this.glyphs[i].width = glyphTemp.Width; this.glyphs[i].height = glyphTemp.Height; this.glyphs[i].offsetX = glyphTemp.Width - glyphTempTypo.Width; if (isSpace) { this.glyphs[i].width /= 2; this.glyphs[i].offsetX /= 2; } atlas[i].X = x; atlas[i].Y = y; atlas[i].W = glyphTemp.Width; atlas[i].H = (this.internalFont.Height + 1); // Draw it onto the font surface glyphTemp.DrawOnto(pixelLayer, BlendMode.Solid, x, y); x += glyphTemp.Width + 2; } } // Adjust colors based on alpha value //for (int i = 0; i < pixelLayer.Data.Length; i++) System.Threading.Tasks.Parallel.For(0, pixelLayer.Data.Length, i => { float factor = pixelLayer.Data[i].A / 255.0f; float invFactor = 1.0f - factor; pixelLayer.Data[i].R = (byte)Math.Min(255.0f, Math.Max(0.0f, this.bgColor.R * invFactor + this.color.R * factor)); pixelLayer.Data[i].G = (byte)Math.Min(255.0f, Math.Max(0.0f, this.bgColor.G * invFactor + this.color.G * factor)); pixelLayer.Data[i].B = (byte)Math.Min(255.0f, Math.Max(0.0f, this.bgColor.B * invFactor + this.color.B * factor)); pixelLayer.Data[i].A = (byte)Math.Min(255.0f, Math.Max(0.0f, this.bgColor.A * invFactor + this.color.A * factor)); }); this.height = this.internalFont.Height; this.ascent = (int)Math.Round(this.internalFont.FontFamily.GetCellAscent(this.internalFont.Style) * this.internalFont.Size / this.internalFont.FontFamily.GetEmHeight(this.internalFont.Style)); this.bodyAscent /= BodyAscentRef.Length; this.descent = (int)Math.Round(this.internalFont.FontFamily.GetCellDescent(this.internalFont.Style) * this.internalFont.GetHeight() / this.internalFont.FontFamily.GetLineSpacing(this.internalFont.Style)); this.baseLine = (int)Math.Round(this.internalFont.FontFamily.GetCellAscent(this.internalFont.Style) * this.internalFont.GetHeight() / this.internalFont.FontFamily.GetLineSpacing(this.internalFont.Style)); bool useNearest = this.hint == RenderHint.Monochrome || !this.filtering; this.pixelData = new Pixmap(pixelLayer); this.pixelData.Atlas = new List<Rect>(atlas); this.texture = new Texture(this.pixelData, Texture.SizeMode.Enlarge, useNearest ? OpenTK.Graphics.OpenGL.TextureMagFilter.Nearest : OpenTK.Graphics.OpenGL.TextureMagFilter.Linear, useNearest ? OpenTK.Graphics.OpenGL.TextureMinFilter.Nearest : OpenTK.Graphics.OpenGL.TextureMinFilter.LinearMipmapLinear); this.mat = new Material(this.hint == RenderHint.Monochrome ? DrawTechnique.Mask : DrawTechnique.Alpha, ColorRgba.White, this.texture); }
private void LoadOrCreatePixelData(int cols, int rows, TextRenderingHint textRenderingHint) { Pixmap.Layer pixelLayer = new Pixmap.Layer(MathF.RoundToInt(cols*this.internalFont.Size*1.2f), MathF.RoundToInt(rows*this.internalFont.Height*1.2f)); Pixmap.Layer glyphTemp; Pixmap.Layer glyphTempTypo; Bitmap bm; Bitmap measureBm = new Bitmap(1, 1); Rect[] atlas = new Rect[SupportedChars.Length]; using (Graphics measureGraphics = Graphics.FromImage(measureBm)) { Brush fntBrush = new SolidBrush(Color.Black); StringFormat formatDef = StringFormat.GenericDefault; formatDef.LineAlignment = StringAlignment.Near; formatDef.FormatFlags = 0; StringFormat formatTypo = StringFormat.GenericTypographic; formatTypo.LineAlignment = StringAlignment.Near; int x = 1; int y = 1; for (int i = 0; i < SupportedChars.Length; ++i) { string str = SupportedChars[i].ToString(CultureInfo.InvariantCulture); bool isSpace = str == " "; SizeF charSize = measureGraphics.MeasureString(str, this.internalFont, pixelLayer.Width, formatDef); // Rasterize a single glyph for rendering bm = new Bitmap((int)Math.Ceiling(Math.Max(1, charSize.Width)), this.internalFont.Height + 1); using (Graphics glyphGraphics = Graphics.FromImage(bm)) { glyphGraphics.Clear(Color.Transparent); glyphGraphics.TextRenderingHint = textRenderingHint; glyphGraphics.DrawString(str, this.internalFont, fntBrush, new RectangleF(0, 0, bm.Width, bm.Height), formatDef); } glyphTemp = new Pixmap.Layer(bm); // Rasterize a single glyph in typographic mode for metric analysis if (!isSpace) { Rectangle glyphTempBounds = glyphTemp.OpaqueBounds(); glyphTemp.SubImage(glyphTempBounds.X, 0, glyphTempBounds.Width, glyphTemp.Height); if (BodyAscentRef.Contains(SupportedChars[i])) this.bodyAscent += glyphTempBounds.Height; bm = new Bitmap((int) Math.Ceiling(Math.Max(1, charSize.Width)), this.internalFont.Height + 1); using (Graphics glyphGraphics = Graphics.FromImage(bm)) { glyphGraphics.Clear(Color.Transparent); glyphGraphics.TextRenderingHint = textRenderingHint; glyphGraphics.DrawString(str, this.internalFont, fntBrush, new RectangleF(0, 0, bm.Width, bm.Height), formatTypo); } glyphTempTypo = new Pixmap.Layer(bm); glyphTempTypo.Crop(true, false); } else { glyphTempTypo = glyphTemp; } // Update xy values if it doesn't fit anymore if (x + glyphTemp.Width + 2 > pixelLayer.Width) { x = 1; y += this.internalFont.Height + MathF.Clamp((int) MathF.Ceiling(this.internalFont.Height*0.1875f), 3, 10); } // Memorize atlas coordinates & glyph data this.maxGlyphWidth = Math.Max(this.maxGlyphWidth, glyphTemp.Width); this.glyphs[i].width = glyphTemp.Width; this.glyphs[i].height = glyphTemp.Height; this.glyphs[i].offsetX = glyphTemp.Width - glyphTempTypo.Width; if (isSpace) { this.glyphs[i].width /= 2; this.glyphs[i].offsetX /= 2; } atlas[i].X = x; atlas[i].Y = y; atlas[i].W = glyphTemp.Width; atlas[i].H = (this.internalFont.Height + 1); // Draw it onto the font surface glyphTemp.DrawOnto(pixelLayer, BlendMode.Solid, x, y); x += glyphTemp.Width + MathF.Clamp((int) MathF.Ceiling(this.internalFont.Height*0.125f), 2, 10); if(glyphTempTypo != glyphTemp) glyphTempTypo.Dispose(); glyphTemp.Dispose(); } } // White out texture except alpha channel. for (int i = 0; i < pixelLayer.Data.Length; i++) { pixelLayer.Data[i].R = 255; pixelLayer.Data[i].G = 255; pixelLayer.Data[i].B = 255; } this.pixelData = new Pixmap(pixelLayer) {Atlas = new List<Rect>(atlas)}; }