public TextExtents MeasureText(ref TextBlock block, TextQuality quality) { // First, check if we have cached this text block. Do not use block_cache.TryGetValue, to avoid thrashing // the user's TextBlockExtents struct. int hashcode = block.GetHashCode(); if (block_cache.ContainsKey(hashcode)) { return(block_cache[hashcode]); } // If this block is not cached, we have to measure it and (potentially) place it in the cache. TextExtents extents = MeasureTextExtents(ref block, quality); if ((block.Options & TextPrinterOptions.NoCache) == 0) { block_cache.Add(hashcode, extents); } return(extents); }
static TextExtents() { Empty = new TextExtents(); }
public void Render(ref TextBlock block, Color color, IGlyphRasterizer rasterizer) { GL.PushAttrib(AttribMask.CurrentBit | AttribMask.TextureBit | AttribMask.EnableBit | AttribMask.ColorBufferBit | AttribMask.DepthBufferBit); GL.Enable(EnableCap.Texture2D); GL.Enable(EnableCap.Blend); SetBlendFunction(); GL.Disable(EnableCap.DepthTest); GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (int)All.Modulate); GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvColor, new Color4(0, 0, 0, 0)); GL.Disable(EnableCap.TextureGenQ); GL.Disable(EnableCap.TextureGenR); GL.Disable(EnableCap.TextureGenS); GL.Disable(EnableCap.TextureGenT); RectangleF position; SetColor(color); int block_hash = block.GetHashCode(); if (block_cache.ContainsKey(block_hash)) { GL.CallList(block_cache[block_hash]); } else { using (TextExtents extents = rasterizer.MeasureText(ref block)) { // Build layout int current = 0; foreach (Glyph glyph in block) { // Do not render whitespace characters or characters outside the clip rectangle. if (glyph.IsWhiteSpace || extents[current].Width == 0 || extents[current].Height == 0) { current++; continue; } else if (!Cache.Contains(glyph)) { Cache.Add(glyph, rasterizer, TextQuality); } CachedGlyphInfo info = Cache[glyph]; position = extents[current++]; // Use the real glyph width instead of the measured one (we want to achieve pixel perfect output). position.Size = info.Rectangle.Size; if (!active_lists.ContainsKey(info.Texture)) { if (inactive_lists.Count > 0) { List <Vector2> list = inactive_lists.Dequeue(); list.Clear(); active_lists.Add(info.Texture, list); } else { active_lists.Add(info.Texture, new List <Vector2>()); } } { // Interleaved array: Vertex, TexCoord, Vertex, ... List <Vector2> current_list = active_lists[info.Texture]; current_list.Add(new Vector2(info.RectangleNormalized.Left, info.RectangleNormalized.Top)); current_list.Add(new Vector2(position.Left, position.Top)); current_list.Add(new Vector2(info.RectangleNormalized.Left, info.RectangleNormalized.Bottom)); current_list.Add(new Vector2(position.Left, position.Bottom)); current_list.Add(new Vector2(info.RectangleNormalized.Right, info.RectangleNormalized.Bottom)); current_list.Add(new Vector2(position.Right, position.Bottom)); current_list.Add(new Vector2(info.RectangleNormalized.Right, info.RectangleNormalized.Bottom)); current_list.Add(new Vector2(position.Right, position.Bottom)); current_list.Add(new Vector2(info.RectangleNormalized.Right, info.RectangleNormalized.Top)); current_list.Add(new Vector2(position.Right, position.Top)); current_list.Add(new Vector2(info.RectangleNormalized.Left, info.RectangleNormalized.Top)); current_list.Add(new Vector2(position.Left, position.Top)); } } } // Render int display_list = 0; if ((block.Options & TextPrinterOptions.NoCache) == 0) { display_list = GL.GenLists(1); // Mesa Indirect gerates an InvalidOperation error right after // GL.EndList() when using ListMode.CompileAndExecute. // Using ListMode.Compile as a workaround. GL.NewList(display_list, ListMode.Compile); } foreach (Texture2D key in active_lists.Keys) { List <Vector2> list = active_lists[key]; key.Bind(); GL.Begin(BeginMode.Triangles); for (int i = 0; i < list.Count; i += 2) { GL.TexCoord2(list[i]); GL.Vertex2(list[i + 1]); } GL.End(); } if ((block.Options & TextPrinterOptions.NoCache) == 0) { GL.EndList(); block_cache.Add(block_hash, display_list); GL.CallList(display_list); } // Clean layout foreach (List <Vector2> list in active_lists.Values) { //list.Clear(); inactive_lists.Enqueue(list); } active_lists.Clear(); } GL.PopAttrib(); }
internal TextExtents Clone() { var extents = new TextExtents(); extents._glyphExtents.AddRange(GlyphExtents); extents.BoundingBox = BoundingBox; return extents; }
TextExtents MeasureTextExtents(ref TextBlock block, TextQuality quality) { // TODO: Parse layout options: var format = block.Font.Italic ? measure_string_format : measure_string_format_tight; //StringFormat format = measure_string_format_tight; if (block.Direction == TextDirection.Vertical) { format.FormatFlags |= StringFormatFlags.DirectionVertical; } else { format.FormatFlags &= ~StringFormatFlags.DirectionVertical; } if (block.Direction == TextDirection.RightToLeft) { format.FormatFlags |= StringFormatFlags.DirectionRightToLeft; } else { format.FormatFlags &= ~StringFormatFlags.DirectionRightToLeft; } if (block.Alignment == TextAlignment.Near) { format.Alignment = StringAlignment.Near; } else if (block.Alignment == TextAlignment.Center) { format.Alignment = StringAlignment.Center; } else { format.Alignment = StringAlignment.Far; } TextExtents extents = text_extents_pool.Acquire(); RectangleF rect = block.Bounds; // Work around Mono/GDI+ bug, which causes incorrect // text wraping when block.Bounds == SizeF.Empty. if (block.Bounds.Size == SizeF.Empty) { rect.Size = MaximumGraphicsClipSize; } SetTextRenderingOptions(graphics, block.Font, quality); IntPtr native_graphics = GdiPlus.GetNativeGraphics(graphics); IntPtr native_font = GdiPlus.GetNativeFont(block.Font); IntPtr native_string_format = GdiPlus.GetNativeStringFormat(format); float max_width = 0, max_height = 0; // It seems that the mere presence of \n and \r characters // is enough for Mono to botch the layout (even if these // characters are not processed.) We'll need to find a // different way to perform layout on Mono, probably // through Pango. // TODO: This workaround allocates memory. //if (Configuration.RunningOnMono) { string[] lines = block.Text.Replace("\r", String.Empty).Split('\n'); foreach (string s in lines) { float width, height; extents.AddRange(MeasureGlyphExtents( ref block, s, native_graphics, native_font, native_string_format, ref rect, out width, out height)); if ((block.Direction & TextDirection.Vertical) == 0) { rect.Y += block.Font.Height; } else { rect.X += block.Font.Height; } if (width > max_width) { max_width = width; } if (height > max_height) { max_height = height; } } } if (extents.Count > 0) { extents.BoundingBox = new RectangleF(extents[0].X, extents[0].Y, max_width, max_height); } else { extents.BoundingBox = RectangleF.Empty; } return(extents); }