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);
        }
Example #2
0
 static TextExtents()
 {
     Empty = new TextExtents();
 }
Example #3
0
        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();
        }
Example #4
0
		static TextExtents()
		{
			Empty = new TextExtents();
		}
Example #5
0
		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);
        }