// Performs layout on the given string. void PerformLayout(string text, TextureFont font, float width, bool wordWarp, StringAlignment alignment, bool rightToLeft, ref Vector2[] vertices, ref ushort[] indices, out int num_indices) { if (text == null) throw new ArgumentNullException("Parameter cannot be null.", "text"); if (text.Length > 8192) throw new ArgumentOutOfRangeException("text", text.Length, "Text length must be between 1 and 8192 characters"); if (wordWarp || rightToLeft || alignment != StringAlignment.Near) throw new NotImplementedException(); while (8 * text.Length > vertices.Length) vertices = new Vector2[vertices.Length << 1]; while (6 * text.Length > indices.Length) indices = new ushort[indices.Length << 1]; num_indices = 6 * text.Length; //Vector2[] vertices = new Vector2[8 * text.Length]; // Interleaved, vertex, texcoord, vertex, etc... //ushort[] indices = new ushort[6 * text.Length]; float x_pos = 0, y_pos = 0; ushort i = 0, index_count = 0, vertex_count = 0, last_break_point = 0; Box2 rect = new Box2(); float char_width, char_height, measured_width, measured_height; int texture; font.LoadGlyphs(text); // Every character comprises of 4 vertices, forming two triangles. We generate an index array which // indexes vertices in a triangle-strip fashion. To create a single strip for the whole string, we // need to add a degenerate triangle (0 height) to connect the last vertex of the previous line with // the first vertex of the next line. // This algorithm works for left-to-right scripts. if (alignment == StringAlignment.Near && !rightToLeft || alignment == StringAlignment.Far && rightToLeft) { foreach (char c in text) { if (Char.IsSeparator(c)) last_break_point = index_count; if (c != '\n' && c != '\r') { font.GlyphData(c, out char_width, out char_height, out rect, out texture); vertices[vertex_count].X = x_pos; // Vertex vertices[vertex_count++].Y = y_pos; vertices[vertex_count].X = rect.Left; // Texcoord vertices[vertex_count++].Y = rect.Top; vertices[vertex_count].X = x_pos; // Vertex vertices[vertex_count++].Y = y_pos + char_height; vertices[vertex_count].X = rect.Left; // Texcoord vertices[vertex_count++].Y = rect.Bottom; vertices[vertex_count].X = x_pos + char_width; // Vertex vertices[vertex_count++].Y = y_pos + char_height; vertices[vertex_count].X = rect.Right; // Texcoord vertices[vertex_count++].Y = rect.Bottom; vertices[vertex_count].X = x_pos + char_width; // Vertex vertices[vertex_count++].Y = y_pos; vertices[vertex_count].X = rect.Right; // Texcoord vertices[vertex_count++].Y = rect.Top; indices[index_count++] = (ushort)(vertex_count - 8); indices[index_count++] = (ushort)(vertex_count - 6); indices[index_count++] = (ushort)(vertex_count - 4); indices[index_count++] = (ushort)(vertex_count - 4); indices[index_count++] = (ushort)(vertex_count - 2); indices[index_count++] = (ushort)(vertex_count - 8); font.MeasureString(text.Substring(i, 1), out measured_width, out measured_height); x_pos += measured_width; } else if (c == '\n') { //x_pos = layoutRect.Left; x_pos = 0; y_pos += font.Height; } ++i; } } else if (alignment != StringAlignment.Center) { throw new NotImplementedException("This feature is not yet implemented. Sorry for the inconvenience."); } else { throw new NotImplementedException("This feature is not yet implemented. Sorry for the inconvenience."); } }
/// <summary> /// Returns the characteristics of a loaded glyph. /// </summary> /// <param name="glyph">The character corresponding to this glyph.</param> /// <param name="width">The width of this glyph.</param> /// <param name="height">The height of this glyph (line spacing).</param> /// <param name="textureRectangle">The bounding box of the texture data of this glyph.</param> /// <param name="texture">The handle to the texture that contains this glyph.</param> /// <returns>True if the glyph has been loaded, false otherwise.</returns> /// <seealso cref="LoadGlyphs"/> public bool GlyphData(char glyph, out float width, out float height, out Box2 textureRectangle, out int texture) { if (loaded_glyphs.TryGetValue(glyph, out textureRectangle)) { width = textureRectangle.Width * texture_width; height = textureRectangle.Height * texture_height; texture = TextureFont.texture; return true; } width = height = texture = 0; return false; }
/// <summary> /// Prepares the specified glyph for rendering. /// </summary> /// <param name="glyphs">The glyph to prepare for rendering.</param> public void LoadGlyph(char glyph) { Box2 rect = new Box2(); if (!loaded_glyphs.ContainsKey(glyph)) LoadGlyph(glyph, out rect); }
/// <summary> /// Adds a glyph to the texture packer. /// </summary> /// <param name="c">The character of the glyph.</param> /// <param name="rectangle">An OpenTK.Math.Box2 that will hold the data for this glyph.</param> private void LoadGlyph(char c, out Box2 rectangle) { if (pack == null) PrepareTexturePacker(); Glyph g = new Glyph(c, font); Rectangle rect = new Rectangle(); try { pack.Add(g, out rect); } catch (InvalidOperationException expt) { // TODO: The TexturePacker is full, create a new font sheet. Trace.WriteLine(expt); throw; } GL.BindTexture(TextureTarget.Texture2d, texture); gfx.Clear(System.Drawing.Color.Transparent); gfx.DrawString(g.Character.ToString(), g.Font, System.Drawing.Brushes.White, 0.0f, 0.0f); //BitmapData bitmap_data = bitmap.LockBits(new Rectangle(0, 0, rect.Width, rect.Height), ImageLockMode.ReadOnly, // System.Drawing.Imaging.PixelFormat.Format32bppArgb); //GL.TexSubImage2D(TextureTarget.Texture2d, 0, rect.Left, rect.Top, rect.Width, rect.Height, // OpenTK.OpenGL.Enums.PixelFormat.Rgba, PixelType.UnsignedByte, bitmap_data.Scan0); //bitmap.UnlockBits(bitmap_data); BitmapData bitmap_data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); int needed_size = rect.Width * rect.Height; if (data.Length < needed_size) Array.Resize<int>(ref data, needed_size); Array.Clear(data, 0, needed_size); unsafe { int* bitmap_data_ptr = (int*)bitmap_data.Scan0; for (int y = 0; y < rect.Height; y++) { for (int x = 0; x < rect.Width; x++) { data[y * rect.Width + x] = *(bitmap_data_ptr + y * bmp.Width + x); } } fixed (int* data_ptr = data) GL.TexSubImage2D(TextureTarget.Texture2d, 0, rect.Left, rect.Top, rect.Width, rect.Height, OpenTK.OpenGL.Enums.PixelFormat.Rgba, PixelType.UnsignedByte, (IntPtr)data_ptr); } bmp.UnlockBits(bitmap_data); rectangle = new Box2( rect.Left / (float)texture_width, rect.Top / (float)texture_height, rect.Right / (float)texture_width, rect.Bottom / (float)texture_height); loaded_glyphs.Add(g.Character, rectangle); }
/// <summary> /// Prepares the specified glyphs for rendering. /// </summary> /// <param name="glyphs">The glyphs to prepare for rendering.</param> public void LoadGlyphs(string glyphs) { Box2 rect = new Box2(); foreach (char c in glyphs) { if (!loaded_glyphs.ContainsKey(c)) LoadGlyph(c, out rect); } }