Beispiel #1
0
        /// <summary>
        /// Get the kerning offset between two <see cref="Glyph"/>.
        /// </summary>
        /// <param name="first">The unicode code point of the first character.</param>
        /// <param name="second">The unicode code point of the second character.</param>
        /// <param name="characterSize">Reference character size.</param>
        /// <returns>Kerning value for \a first and \a second, in pixels.</returns>
        public float GetKerning(int first, int second, int characterSize)
        {
            // Special case where first or second is 0 (null character)
            if (first == 0 || second == 0)
            {
                return(0f);
            }

            if (_face != null && _face.HasKerning && SetCurrentSize(characterSize))
            {
                // Convert the characters to indices
                uint index1 = _face.GetCharIndex((uint)first);
                uint index2 = _face.GetCharIndex((uint)second);

                // Get the kerning vector
                var kerning = _face.GetKerning(index1, index2, SharpFont.KerningMode.Default);

                // X advance is already in pixels for bitmap fonts
                if (!_face.IsScalable)
                {
                    return(kerning.X.ToInt32());
                }

                // Return the X advance
                return(kerning.X.ToSingle());
            }
            else
            {
                // Invalid font, or no kerning
                return(0f);
            }
        }
Beispiel #2
0
        private bool GetGlyphIndex(char c, out GlyphInfo gli)
        {
            GlyphInfo info;

            if (CharMap.TryGetValue(c, out info))
            {
                if (info.ListID > 0 && info.Width > 0)
                {
                    gli = info;
                    return(true);
                }
                else
                {
                    gli = GlyphInfo.Empty;
                    return(false);
                }
            }
            else if (OnDemand)
            {
                lock (SyncObject) {
                    uint glyphindex = Font.GetCharIndex(c);
                    info = CompileCharacter(Font, glyphindex, c);
                    CharMap.Add(c, info);
                    gli = info;
                }
                return(true);
            }

            gli = GlyphInfo.Empty;
            return(false);
        }
Beispiel #3
0
        public FontDef(Face face, int size)
        {
            const int atlasSize = 1024;
            atlas = new RenderTarget2D(GraphicsManager.device, atlasSize, atlasSize, false, SurfaceFormat.Color, DepthFormat.None, 1, RenderTargetUsage.PreserveContents);
            fontFace = face;
            pxSize = size;

            face.SetPixelSizes((uint)size, (uint)size);
            face.LoadGlyph(face.GetCharIndex('A'), loadFlags, loadTarget);
            lineSize = (int)Math.Ceiling((float)face.Glyph.Metrics.Height * 2f);

            const string defaultCharset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ!@#$%^&*()`-=~_+[]{}\\|;',./:\"<>? ";
            foreach (char c in defaultCharset) AddGlyph(c);
        }
Beispiel #4
0
		// Rasterizes a single character glyph.
		private Glyph ImportGlyph(char character, Face face)
		{
			uint glyphIndex = face.GetCharIndex(character);
			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<byte>(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 = MathHelper.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<byte>(gHA, gVA);
			}

			// not sure about this at all
			var abc = new ABCFloat ();
			abc.A = face.Glyph.Metrics.HorizontalBearingX >> 6;
			abc.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
			};
		}
Beispiel #5
0
        private int renderCharacter(Face face, char character, int posX, int posY, int atlas, FontInfo info, Graphics graphics)
        {
            uint index = face.GetCharIndex(character);
            face.LoadGlyph(index, LoadFlags.Default, LoadTarget.Normal);
            face.Glyph.RenderGlyph(RenderMode.Normal);

            GlyphMetrics metrics = face.Glyph.Metrics;
            int width = metrics.Width.ToInt32() + metrics.HorizontalBearingX.ToInt32();
            int xAdvance = metrics.HorizontalAdvance.ToInt32();
            int yoffset = metrics.VerticalAdvance.ToInt32() - metrics.HorizontalBearingY.ToInt32();
            int charHeight = metrics.Height.ToInt32();

            if (face.Glyph.Bitmap.Width > 0)
            {
                FTBitmap ftbmp = face.Glyph.Bitmap;
                Bitmap copy = ftbmp.ToGdipBitmap(Color.White);
                graphics.DrawImageUnscaled(copy, posX + metrics.HorizontalBearingX.ToInt32(), posY);
            }

            info.addCharacter(new CharacterInfo(character, posX, posY, width, charHeight, xAdvance, yoffset, atlas));

            return width;
        }
Beispiel #6
0
        private void getKernWidthHeight(Face face, FontInfo info, out int width, out int maxHeight, out int firstAdjust)
        {
            width = 0;
            maxHeight = 0;
            firstAdjust = 0;

            for (int i = 0; i < characters.Length; i++)
            {
                char character1 = characters[i];
                uint index1 = face.GetCharIndex(character1);

                face.LoadGlyph(index1, LoadFlags.Default, LoadTarget.Normal);
                GlyphMetrics metrics = face.Glyph.Metrics;

                int yoffset = metrics.VerticalAdvance.ToInt32() - metrics.HorizontalBearingY.ToInt32();
                int nheight = yoffset + metrics.Height.ToInt32();
                int height = metrics.Height.ToInt32();
                if (height > maxHeight)
                {
                    maxHeight = height; //nheight;
                    firstAdjust = yoffset;
                }

                for (int j = 0; j < characters.Length; j++)
                {
                    char character2 = characters[j];
                    uint index2 = face.GetCharIndex(character2);

                    FTVector26Dot6 kern = face.GetKerning(index1, index2, KerningMode.Default);
                    int kernX = kern.X.ToInt32();
                    int kernY = kern.Y.ToInt32();
                    if (kernX != 0 || kernY != 0)
                    {
                        info.addKerning(new KerningInfo(character1, character2, kernX, kernY));
                    }
                }

                if (i + 1 == characters.Length)
                {
                    width += metrics.Width.ToInt32() + metrics.HorizontalBearingX.ToInt32() + padX;
                }
                else
                {
                    width += metrics.HorizontalAdvance.ToInt32() + padX;
                }
            }
        }
Beispiel #7
0
        public static Bitmap RenderString(Library library, Face face, string text)
        {
            float penX = 0, penY = 0;
            float width = 0;
            float height = 0;

            //measure the size of the string before rendering it, requirement of Bitmap.
            for (int i = 0; i < text.Length; i++)
            {
                char c = text[i];

                uint glyphIndex = face.GetCharIndex(c);
                face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);

                width += (float)face.Glyph.Advance.X;

                if (face.HasKerning && i < text.Length - 1)
                {
                    char cNext = text[i + 1];
                    width += (float)face.GetKerning(glyphIndex, face.GetCharIndex(cNext), KerningMode.Default).X;
                }

                if ((float)face.Glyph.Metrics.Height > height)
                    height = (float)face.Glyph.Metrics.Height;
            }

            //create a new bitmap that fits the string.
            Bitmap bmp = new Bitmap((int)Math.Ceiling(width), (int)Math.Ceiling(height));
            Graphics g = Graphics.FromImage(bmp);
            g.Clear(SystemColors.Control);

            //draw the string
            for (int i = 0; i < text.Length; i++)
            {
                char c = text[i];

                uint glyphIndex = face.GetCharIndex(c);
                face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
                face.Glyph.RenderGlyph(RenderMode.Normal);

                if (c == ' ')
                {
                    penX += (float)face.Glyph.Advance.X;

                    if (face.HasKerning && i < text.Length - 1)
                    {
                        char cNext = text[i + 1];
                        width += (float)face.GetKerning(glyphIndex, face.GetCharIndex(cNext), KerningMode.Default).X;
                    }

                    penY += (float)face.Glyph.Advance.Y;
                    continue;
                }

                //FTBitmap ftbmp = face.Glyph.Bitmap.Copy(library);
                FTBitmap ftbmp = face.Glyph.Bitmap;
                Bitmap cBmp = ftbmp.ToGdipBitmap(Color.Black);

                //Not using g.DrawImage because some characters come out blurry/clipped.
                //g.DrawImage(cBmp, penX + face.Glyph.BitmapLeft, penY + (bmp.Height - face.Glyph.Bitmap.Rows));
                g.DrawImageUnscaled(cBmp, (int)Math.Round(penX + face.Glyph.BitmapLeft), (int)Math.Round(penY + (bmp.Height - face.Glyph.BitmapTop)));

                penX += (float)face.Glyph.Metrics.HorizontalAdvance;
                penY += (float)face.Glyph.Advance.Y;

                if (face.HasKerning && i < text.Length - 1)
                {
                    char cNext = text[i + 1];
                    var kern = face.GetKerning(glyphIndex, face.GetCharIndex(cNext), KerningMode.Default);
                    penX += (float)kern.X;
                }
            }

            g.Dispose();
            return bmp;
        }
        void BuildTextureOld()
        {
            #region attribution
            // Significant parts of this method were based off the following example:
            // https://github.com/Robmaister/SharpFont/blob/master/Source/Examples/Program.cs
            // The following license and attribution applies to those parts:

            /*Copyright (c) 2012 Robert Rouhani <*****@*****.**>
            Permission is hereby granted, free of charge, to any person obtaining a copy of
            this software and associated documentation files (the "Software"), to deal in
            the Software without restriction, including without limitation the rights to
            use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
            of the Software, and to permit persons to whom the Software is furnished to do
            so, subject to the following conditions:
            The above copyright notice and this permission notice shall be included in all
            copies or substantial portions of the Software.
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
            IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
            FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
            AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
            LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
            OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
            SOFTWARE.*/
            #endregion

            Face face = new Face(ftLib, "C:\\Windows\\Fonts\\segoeui.ttf");
            face.SetCharSize(0, 14, 72, 72);
            //face.SetPixelSizes(0, 14);

            const LoadFlags loadFlags = LoadFlags.Default;
            const LoadTarget loadTarget = LoadTarget.Light;
            const RenderMode renderMode = RenderMode.Light;

            float cx = 0, cy = 0, tw = 0, th = 0;

            // base size
            face.LoadGlyph(face.GetCharIndex('A'), loadFlags, loadTarget);
            th = (float)face.Glyph.Metrics.Height;

            for (int i = 0; i < text.Length; i++) {
                char c = text[i];
                uint cnum = face.GetCharIndex(c);
                face.LoadGlyph(cnum, loadFlags, loadTarget);
                tw += (float)face.Glyph.Advance.X;
                if (face.HasKerning && i < text.Length - 1)  tw += (float)face.GetKerning(cnum, face.GetCharIndex(text[i + 1]), KerningMode.Default).X;
                th = Math.Max(th, (float)face.Glyph.Metrics.Height);

            }
            if (tw == 0) tw = th;

            Bitmap bmp = new Bitmap((int)Math.Ceiling(tw), (int)Math.Ceiling(th*2)); // assumption that any downstem is at most half the height of a capital letter
            cy = th * 0.5f; // adjustment for such
            //Bitmap bmp = new Bitmap(128, 32);
            System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmp);
            g.Clear(System.Drawing.Color.Transparent);
            //g.Clear(System.Drawing.Color.DimGray);

            for (int i = 0; i < text.Length; i++) {
                char c = text[i];
                uint cnum = face.GetCharIndex(c);
                face.LoadGlyph(cnum, loadFlags, loadTarget);
                face.Glyph.RenderGlyph(renderMode);
                if (c == ' ') {
                    cx += (float)face.Glyph.Advance.X;
                    // omitted an if statement that didn't seem to do anything relevant??
                    cy += (float)face.Glyph.Advance.Y;
                    continue;
                }

                FTBitmap ftb = face.Glyph.Bitmap; // lol ftb
                Bitmap cb = ftb.ToGdipBitmap(System.Drawing.Color.White);
                g.DrawImageUnscaled(cb, (int)Math.Round(cx + face.Glyph.BitmapLeft), (int)Math.Round(cy + (Math.Ceiling(th) - face.Glyph.BitmapTop))); // th and not bitmap.height
                cx += (float)face.Glyph.Metrics.HorizontalAdvance;
                cy += (float)face.Glyph.Advance.Y;
                if (face.HasKerning && i < text.Length - 1) cx += (float)face.GetKerning(cnum, face.GetCharIndex(text[i + 1]), KerningMode.Default).X;
            }
            g.Dispose();
            if (texture != null) texture.Dispose();
            //texture = GraphicsManager.ConvertToPreMultipliedAlphaGPU(GetTexture(GraphicsManager.device, bmp));
            texture = GetTexture(GraphicsManager.device, bmp);
        }
Beispiel #9
0
        public static Bitmap RenderString(Face face, string text)
        {
            int penX = 0, penY = 0;
            int width = 0;
            int height = 0;

            //measure the size of the string before rendering it, requirement of Bitmap.
            for (int i = 0; i < text.Length; i++)
            {
                char c = text[i];

                uint glyphIndex = face.GetCharIndex(c);
                face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);

                width += (int)face.Glyph.Advance.X >> 6;

                if (face.HasKerning && i < text.Length - 1)
                {
                    char cNext = text[i + 1];
                    width += (int)face.GetKerning(glyphIndex, face.GetCharIndex(cNext), KerningMode.Default).X >> 6;
                }

                if (face.Glyph.Metrics.Height >> 6 > height)
                    height = (int)face.Glyph.Metrics.Height >> 6;
            }

            //create a new bitmap that fits the string.
            Bitmap bmp = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(bmp);
            g.Clear(Color.Gray);

            //draw the string
            for (int i = 0; i < text.Length; i++)
            {
                char c = text[i];

                uint glyphIndex = face.GetCharIndex(c);
                face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
                face.Glyph.RenderGlyph(RenderMode.Normal);

                if (c == ' ')
                {
                    penX += (int)face.Glyph.Advance.X >> 6;

                    if (face.HasKerning && i < text.Length - 1)
                    {
                        char cNext = text[i + 1];
                        width += (int)face.GetKerning(glyphIndex, face.GetCharIndex(cNext), KerningMode.Default).X >> 6;
                    }

                    penY += (int)face.Glyph.Advance.Y >> 6;
                    continue;
                }

                Bitmap cBmp = face.Glyph.Bitmap.ToGdipBitmap(Color.Firebrick);
                g.DrawImageUnscaled(cBmp, penX, penY + (bmp.Height - face.Glyph.Bitmap.Rows));

                penX += (int)face.Glyph.Advance.X >> 6;
                penY += (int)face.Glyph.Advance.Y >> 6;

                if (face.HasKerning && i < text.Length - 1)
                {
                    char cNext = text[i + 1];
                    penX += (int)face.GetKerning(glyphIndex, face.GetCharIndex(cNext), KerningMode.Default).X >> 6;
                }
            }

            g.Dispose();
            return bmp;
        }
Beispiel #10
0
        internal static Bitmap RenderString(Library library, Face face, string text, Color foreColor, Color backColor)
        {
            var measuredChars = new List<DebugChar>();
            var renderedChars = new List<DebugChar>();
            float penX = 0, penY = 0;
            float stringWidth = 0; // the measured width of the string
            float stringHeight = 0; // the measured height of the string
            float overrun = 0;
            float underrun = 0;
            float kern = 0;
            int spacingError = 0;
            bool trackingUnderrun = true;
            int rightEdge = 0; // tracking rendered right side for debugging

            // Bottom and top are both positive for simplicity.
            // Drawing in .Net has 0,0 at the top left corner, with positive X to the right
            // and positive Y downward.
            // Glyph metrics have an origin typically on the left side and at baseline
            // of the visual data, but can draw parts of the glyph in any quadrant, and
            // even move the origin (via kerning).
            float top = 0, bottom = 0;

            // Measure the size of the string before rendering it. We need to do this so
            // we can create the proper size of bitmap (canvas) to draw the characters on.
            for (int i = 0; i < text.Length; i++)
            {
                #region Load character
                char c = text[i];

                // Look up the glyph index for this character.
                uint glyphIndex = face.GetCharIndex(c);

                // Load the glyph into the font's glyph slot. There is usually only one slot in the font.
                face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);

                // Refer to the diagram entitled "Glyph Metrics" at http://www.freetype.org/freetype2/docs/tutorial/step2.html.
                // There is also a glyph diagram included in this example (glyph-dims.svg).
                // The metrics below are for the glyph loaded in the slot.
                float gAdvanceX = (float)face.Glyph.Advance.X; // same as the advance in metrics
                float gBearingX = (float)face.Glyph.Metrics.HorizontalBearingX;
                float gWidth = face.Glyph.Metrics.Width.ToSingle();
                var rc = new DebugChar(c, gAdvanceX, gBearingX, gWidth);
                #endregion
                #region Underrun
                // Negative bearing would cause clipping of the first character
                // at the left boundary, if not accounted for.
                // A positive bearing would cause empty space.
                underrun += -(gBearingX);
                if (stringWidth == 0)
                    stringWidth += underrun;
                if (trackingUnderrun)
                    rc.Underrun = underrun;
                if (trackingUnderrun && underrun <= 0)
                {
                    underrun = 0;
                    trackingUnderrun = false;
                }
                #endregion
                #region Overrun
                // Accumulate overrun, which coould cause clipping at the right side of characters near
                // the end of the string (typically affects fonts with slanted characters)
                if (gBearingX + gWidth > 0 || gAdvanceX > 0)
                {
                    overrun -= Math.Max(gBearingX + gWidth, gAdvanceX);
                    if (overrun <= 0) overrun = 0;
                }
                overrun += (float)(gBearingX == 0 && gWidth == 0 ? 0 : gBearingX + gWidth - gAdvanceX);
                // On the last character, apply whatever overrun we have to the overall width.
                // Positive overrun prevents clipping, negative overrun prevents extra space.
                if (i == text.Length - 1)
                    stringWidth += overrun;
                rc.Overrun = overrun; // accumulating (per above)
                #endregion

                #region Top/Bottom
                // If this character goes higher or lower than any previous character, adjust
                // the overall height of the bitmap.
                float glyphTop = (float)face.Glyph.Metrics.HorizontalBearingY;
                float glyphBottom = (float)(face.Glyph.Metrics.Height - face.Glyph.Metrics.HorizontalBearingY);
                if (glyphTop > top)
                    top = glyphTop;
                if (glyphBottom > bottom)
                    bottom = glyphBottom;
                #endregion

                // Accumulate the distance between the origin of each character (simple width).
                stringWidth += gAdvanceX;
                rc.RightEdge = stringWidth;
                measuredChars.Add(rc);

                #region Kerning (for NEXT character)
                // Calculate kern for the NEXT character (if any)
                // The kern value adjusts the origin of the next character (positive or negative).
                if (face.HasKerning && i < text.Length - 1)
                {
                    char cNext = text[i + 1];
                    kern = (float)face.GetKerning(glyphIndex, face.GetCharIndex(cNext), KerningMode.Default).X;
                    // sanity check for some fonts that have kern way out of whack
                    if (kern > gAdvanceX * 5 || kern < -(gAdvanceX * 5))
                        kern = 0;
                    rc.Kern = kern;
                    stringWidth += kern;
                }

                #endregion
            }

            stringHeight = top + bottom;

            // If any dimension is 0, we can't create a bitmap
            if (stringWidth == 0 || stringHeight == 0)
                return null;

            // Create a new bitmap that fits the string.
            Bitmap bmp = new Bitmap((int)Math.Ceiling(stringWidth), (int)Math.Ceiling(stringHeight));
            trackingUnderrun = true;
            underrun = 0;
            overrun = 0;
            stringWidth = 0;
            using (var g = Graphics.FromImage(bmp))
            {
                #region Set up graphics
                // HighQuality and GammaCorrected both specify gamma correction be applied (2.2 in sRGB)
                // https://msdn.microsoft.com/en-us/library/windows/desktop/ms534094(v=vs.85).aspx
                g.CompositingQuality = CompositingQuality.HighQuality;
                // HighQuality and AntiAlias both specify antialiasing
                g.SmoothingMode = SmoothingMode.HighQuality;
                // If a background color is specified, blend over it.
                g.CompositingMode = CompositingMode.SourceOver;

                g.Clear(backColor);
                #endregion

                // Draw the string into the bitmap.
                // A lot of this is a repeat of the measuring steps, but this time we have
                // an actual bitmap to work with (both canvas and bitmaps in the glyph slot).
                for (int i = 0; i < text.Length; i++)
                {
                    #region Load character
                    char c = text[i];

                    // Same as when we were measuring, except RenderGlyph() causes the glyph data
                    // to be converted to a bitmap.
                    uint glyphIndex = face.GetCharIndex(c);
                    face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
                    face.Glyph.RenderGlyph(RenderMode.Normal);
                    FTBitmap ftbmp = face.Glyph.Bitmap;

                    float gAdvanceX = (float)face.Glyph.Advance.X;
                    float gBearingX = (float)face.Glyph.Metrics.HorizontalBearingX;
                    float gWidth = (float)face.Glyph.Metrics.Width;

                    var rc = new DebugChar(c, gAdvanceX, gBearingX, gWidth);
                    #endregion
                    #region Underrun
                    // Underrun
                    underrun += -(gBearingX);
                    if (penX == 0)
                        penX += underrun;
                    if (trackingUnderrun)
                        rc.Underrun = underrun;
                    if (trackingUnderrun && underrun <= 0)
                    {
                        underrun = 0;
                        trackingUnderrun = false;
                    }
                    #endregion
                    #region Draw glyph
                    // Whitespace characters sometimes have a bitmap of zero size, but a non-zero advance.
                    // We can't draw a 0-size bitmap, but the pen position will still get advanced (below).
                    if ((ftbmp.Width > 0 && ftbmp.Rows > 0))
                    {
                        // Get a bitmap that .Net can draw (GDI+ in this case).
                        Bitmap cBmp = ftbmp.ToGdipBitmap(foreColor);
                        rc.Width = cBmp.Width;
                        rc.BearingX = face.Glyph.BitmapLeft;
                        int x = (int)Math.Round(penX + face.Glyph.BitmapLeft);
                        int y = (int)Math.Round(penY + top - (float)face.Glyph.Metrics.HorizontalBearingY);
                        //Not using g.DrawImage because some characters come out blurry/clipped. (Is this still true?)
                        g.DrawImageUnscaled(cBmp, x, y);
                        rc.Overrun = face.Glyph.BitmapLeft + cBmp.Width - gAdvanceX;
                        // Check if we are aligned properly on the right edge (for debugging)
                        rightEdge = Math.Max(rightEdge, x + cBmp.Width);
                        spacingError = bmp.Width - rightEdge;
                    }
                    else
                    {
                        rightEdge = (int)(penX + gAdvanceX);
                        spacingError = bmp.Width - rightEdge;
                    }
                    #endregion

                    #region Overrun
                    if (gBearingX + gWidth > 0 || gAdvanceX > 0)
                    {
                        overrun -= Math.Max(gBearingX + gWidth, gAdvanceX);
                        if (overrun <= 0) overrun = 0;
                    }
                    overrun += (float)(gBearingX == 0 && gWidth == 0 ? 0 : gBearingX + gWidth - gAdvanceX);
                    if (i == text.Length - 1) penX += overrun;
                    rc.Overrun = overrun;
                    #endregion

                    // Advance pen positions for drawing the next character.
                    penX += (float)face.Glyph.Advance.X; // same as Metrics.HorizontalAdvance?
                    penY += (float)face.Glyph.Advance.Y;

                    rc.RightEdge = penX;
                    spacingError = bmp.Width - (int)Math.Round(rc.RightEdge);
                    renderedChars.Add(rc);

                    #region Kerning (for NEXT character)
                    // Adjust for kerning between this character and the next.
                    if (face.HasKerning && i < text.Length - 1)
                    {
                        char cNext = text[i + 1];
                        kern = (float)face.GetKerning(glyphIndex, face.GetCharIndex(cNext), KerningMode.Default).X;
                        if (kern > gAdvanceX * 5 || kern < -(gAdvanceX * 5))
                            kern = 0;
                        rc.Kern = kern;
                        penX += (float)kern;
                    }
                    #endregion

                }

            }
            bool printedHeader = false;
            if (spacingError != 0)
            {
                for (int i = 0; i < renderedChars.Count; i++)
                {
                    //if (measuredChars[i].RightEdge != renderedChars[i].RightEdge)
                    //{
                    if (!printedHeader)
                        DebugChar.PrintHeader();
                    printedHeader = true;
                    Debug.Print(measuredChars[i].ToString());
                    Debug.Print(renderedChars[i].ToString());
                    //}
                }
                string msg = string.Format("Right edge: {0,3} ({1}) {2}",
                    spacingError,
                    spacingError == 0 ? "perfect" : spacingError > 0 ? "space  " : "clipped",
                    face.FamilyName);
                System.Diagnostics.Debug.Print(msg);
                //throw new ApplicationException(msg);
            }
            return bmp;
        }
		// Rasterizes a single character glyph.
		static Glyph ImportGlyph(char character, Face face, Brush brush, StringFormat stringFormat, Bitmap bitmap, System.Drawing.Graphics graphics)
		{
			string characterString = character.ToString();

			uint glyphIndex = face.GetCharIndex(character);
			face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
			face.Glyph.RenderGlyph(RenderMode.Normal);

			// Measure the size of this character.
			var width = (int)face.Glyph.Advance.X >> 6;
			var height = (int)face.Glyph.Metrics.Height >> 6;

			SizeF size = new SizeF(width, height);

			int characterWidth = (int)Math.Ceiling(size.Width);
			int characterHeight = (int)Math.Ceiling(size.Height);

			// Pad to make sure we capture any overhangs (negative ABC spacing, etc.)
			int padWidth = characterWidth;
			int padHeight = characterHeight / 2;

			int bitmapWidth = characterWidth + padWidth * 2;
			int bitmapHeight = characterHeight + padHeight * 2;

			if (bitmapWidth > MaxGlyphSize || bitmapHeight > MaxGlyphSize)
				throw new Exception("Excessively large glyph won't fit in my lazily implemented fixed size temp surface.");

			// Render the character.
			graphics.Clear(System.Drawing.Color.Black);

			if (face.Glyph.Bitmap.Width > 0 && face.Glyph.Bitmap.Rows > 0) {
				BitmapData data = bitmap.LockBits (new System.Drawing.Rectangle (0, 0, face.Glyph.Bitmap.Width, face.Glyph.Bitmap.Rows), 
				                                 ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
				byte[] gpixelAlphas = new byte[face.Glyph.Bitmap.Width * face.Glyph.Bitmap.Rows];
				Marshal.Copy (face.Glyph.Bitmap.Buffer, gpixelAlphas, 0, gpixelAlphas.Length);

				for (int j = 0; j < gpixelAlphas.Length; j++) {
					int pixelOffset = (j / data.Width) * data.Stride + (j % data.Width * 4);
					Marshal.WriteByte (data.Scan0, pixelOffset + 3, gpixelAlphas [j]);
				}

				bitmap.UnlockBits (data);

			}

			// Clone the newly rendered image.
			Bitmap glyphBitmap = null;
			if (face.Glyph.Bitmap.Width > 0 || face.Glyph.Bitmap.Rows > 0)
				glyphBitmap = bitmap.Clone(new System.Drawing.Rectangle(0, 0, face.Glyph.Bitmap.Width, face.Glyph.Bitmap.Rows), PixelFormat.Format32bppArgb);
			else 
			{
				var gHA = face.Glyph.Metrics.HorizontalAdvance >> 6;
				var gVA = face.Glyph.Metrics.VerticalAdvance >> 6;

				gHA = gHA > 0 ? gHA : gVA;
				gVA = gVA > 0 ? gVA : gHA;

				glyphBitmap = new Bitmap (gHA, gVA);
			}

			BitmapUtils.ConvertToGrey(glyphBitmap);

			// not sure about this at all
			var abc = new ABCFloat ();
			abc.A = face.Glyph.Metrics.HorizontalBearingX >> 6;
			abc.B = face.Glyph.Metrics.Width >> 6;
			abc.C = (face.Glyph.Metrics.HorizontalAdvance >> 6) - (abc.A + abc.B);

			// Query its ABC spacing.
			//float? abc = GetCharacterWidth(character, font, graphics);
//			if (glyphBitmap == null)
//				Console.WriteLine("null");


			// Construct the output Glyph object.
			return new Glyph(character, glyphBitmap)
			{
				XOffset = -padWidth,
				XAdvance = face.Glyph.Metrics.HorizontalAdvance >> 6,
                YOffset = -(face.Glyph.Metrics.HorizontalBearingY >> 6),
				CharacterWidths = abc
			};
		}
Beispiel #12
0
        public static Bitmap RenderString(Face face, string text)
        {
            int penX = 0, penY = 0;
            int width = 0;
            int height = 0;

            //measure the size of the string before rendering it, requirement of Bitmap.
            for (int i = 0; i < text.Length; i++)
            {
                char c = text[i];

                uint glyphIndex = face.GetCharIndex(c);
                face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);

                width += (int)face.Glyph.Advance.X >> 6;

                if (face.HasKerning && i < text.Length - 1)
                {
                    char cNext = text[i + 1];
                    width += (int)face.GetKerning(glyphIndex, face.GetCharIndex(cNext), KerningMode.Default).X >> 6;
                }

                if (face.Glyph.Metrics.Height >> 6 > height)
                    height = (int)face.Glyph.Metrics.Height >> 6;
            }

            //create a new bitmap that fits the string.
            Bitmap bmp = new Bitmap(width, height);

            //draw the string
            for (int i = 0; i < text.Length; i++)
            {
                char c = text[i];

                uint glyphIndex = face.GetCharIndex(c);
                face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
                face.Glyph.RenderGlyph(RenderMode.Normal);

                if (c == ' ')
                {
                    penX += (int)face.Glyph.Advance.X >> 6;

                    if (face.HasKerning && i < text.Length - 1)
                    {
                        char cNext = text[i + 1];
                        width += (int)face.GetKerning(glyphIndex, face.GetCharIndex(cNext), KerningMode.Default).X >> 6;
                    }

                    penY += (int)face.Glyph.Advance.Y >> 6;
                    continue;
                }

                BitmapData data = bmp.LockBits(new Rectangle(penX, penY + (bmp.Height - face.Glyph.Bitmap.Rows), face.Glyph.Bitmap.Width, face.Glyph.Bitmap.Rows), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
                byte[] pixelAlphas = new byte[face.Glyph.Bitmap.Width * face.Glyph.Bitmap.Rows];
                Marshal.Copy(face.Glyph.Bitmap.Buffer, pixelAlphas, 0, pixelAlphas.Length);

                for (int j = 0; j < pixelAlphas.Length; j++)
                {
                    int pixelOffset = (j / data.Width) * data.Stride + (j % data.Width * 4);
                    Marshal.WriteByte(data.Scan0, pixelOffset + 3, pixelAlphas[j]);
                }

                bmp.UnlockBits(data);

                penX += (int)face.Glyph.Advance.X >> 6;
                penY += (int)face.Glyph.Advance.Y >> 6;

                if (face.HasKerning && i < text.Length - 1)
                {
                    char cNext = text[i + 1];
                    width += (int)face.GetKerning(glyphIndex, face.GetCharIndex(cNext), KerningMode.Default).X >> 6;
                }
            }

            return bmp;
        }
		// Rasterizes a single character glyph.
		static Glyph ImportGlyph(char character, Face face, Brush brush, StringFormat stringFormat, Bitmap bitmap, System.Drawing.Graphics graphics)
		{
			uint glyphIndex = face.GetCharIndex(character);
			face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
			face.Glyph.RenderGlyph(RenderMode.Normal);

			// Render the character.
			graphics.Clear(System.Drawing.Color.Black);

			if (face.Glyph.Bitmap.Width > 0 && face.Glyph.Bitmap.Rows > 0) {
				BitmapData data = bitmap.LockBits (new System.Drawing.Rectangle (0, 0, face.Glyph.Bitmap.Width, face.Glyph.Bitmap.Rows), 
				                                 ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
				byte[] gpixelAlphas = new byte[face.Glyph.Bitmap.Width * face.Glyph.Bitmap.Rows];
				Marshal.Copy (face.Glyph.Bitmap.Buffer, gpixelAlphas, 0, gpixelAlphas.Length);

				for (int j = 0; j < gpixelAlphas.Length; j++) {
					int pixelOffset = (j / data.Width) * data.Stride + (j % data.Width * 4);
					Marshal.WriteByte (data.Scan0, pixelOffset + 3, gpixelAlphas [j]);
				}

				bitmap.UnlockBits (data);

			}

			// Clone the newly rendered image.
			Bitmap glyphBitmap = null;
			if (face.Glyph.Bitmap.Width > 0 || face.Glyph.Bitmap.Rows > 0)
				glyphBitmap = bitmap.Clone(new System.Drawing.Rectangle(0, 0, face.Glyph.Bitmap.Width, face.Glyph.Bitmap.Rows), PixelFormat.Format32bppArgb);
			else 
			{
				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 Bitmap (gHA, gVA);
			}

			BitmapUtils.ConvertAlphaToGrey(glyphBitmap);

			// not sure about this at all
			var abc = new ABCFloat ();
			abc.A = face.Glyph.Metrics.HorizontalBearingX >> 6;
			abc.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
			};
		}
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            string hello = "The quick brown fox jumps over the lazy dog.";

            charCache = new Dictionary<char, GlyphData>();

            Library lib = new Library();
            Face fontFace = new Face(lib, "fonts/roboto-regular.ttf");

            fontFace.SetCharSize(0, 18 * 64, 0, 96);

            int wX          = 0;
            int wY          = 0;

            int xB          = 0;

            int pX          = 0;
            int pY          = 0;

            uint current    = 0;
            uint previous   = 0;

            GlyphData       data;

            DateTime iTimeS = DateTime.Now;

            foreach (var item in hello)
            {
                current = fontFace.GetCharIndex(item);

                if (!charCache.ContainsKey(item))
                {
                    fontFace.LoadGlyph(current, LoadFlags.Default, LoadTarget.Normal);
                    fontFace.Glyph.RenderGlyph(RenderMode.Normal);

                    FTBitmap bitmap = fontFace.Glyph.Bitmap;

                    data = new GlyphData();

                    if (item != ' ')
                        data.buffer = bitmap.BufferData;

                    data.charIndex  = current;

                    data.metrics    = fontFace.Glyph.Metrics;

                    data.height     = fontFace.Glyph.Metrics.Height / 64;
                    data.width      = fontFace.Glyph.Metrics.Width / 64;

                    data.pitch      = fontFace.Glyph.BitmapTop;
                    data.forward    = fontFace.Glyph.BitmapLeft;

                    data.aX         = fontFace.Glyph.Metrics.HorizontalAdvance;
                    data.aY         = fontFace.Glyph.Metrics.VerticalAdvance;

                    charCache.Add(item, data);
                }
                else
                {
                    data = charCache[item];
                }

                wX     += data.aX / 64;

                if (fontFace.HasKerning && previous != 0 && current != 0)
                {
                    var kInfo = fontFace.GetKerning(previous, current, KerningMode.Default);

                    wX += kInfo.X / 64;

                    data.kX = kInfo.X / 64;
                    data.kY = kInfo.Y / 64;
                }

                previous = current;
            }

            xB = Math.Abs(fontFace.Size.Metrics.Descender / 64);
            wY = fontFace.Size.Metrics.Height / 64;

            DateTime iTimeF = DateTime.Now;

            Console.WriteLine("Time in letting freetype render the glyphs: {0} milliseconds.", (iTimeF - iTimeS).TotalMilliseconds);

            bmp = new Bitmap(wX, wY);

            int[] map = new int[wX * wY];

            for (int i = 0; i < map.Length; i++ )
            {
                map[i] = 0;
            }

            current     = 0;
            previous    = 0;

            DateTime timeS = DateTime.Now;

            for (int i = 0; i < hello.Length; i++)
            {
                GlyphData pdata = charCache[hello[i]];

                for (int y = 0; y < pdata.height; y++)
                {
                    for (int x = 0; x < pdata.width; x++)
                    {
                        int fx = x + pX + pdata.forward;
                        int fy = wY - pdata.pitch + y - xB;

                        fy = y + (wY - pdata.pitch - xB);

                        if (fy < 0 || fy >= wY)
                        {
                            //Console.WriteLine("Overflow on fy! :(");
                            continue;
                        }

                        if (fx < 0 || fx >= wX)
                        {
                            //Console.WriteLine("Overflow on fx! :(");
                            continue;
                        }

                        int fdata = pdata.buffer[y * pdata.width + x] + map[fy * wX + fx];

                        if (fdata >= 255)
                            fdata = 255;

                        map[fy * wX + fx] = (fdata << 24);
                    }
                }

                pX += pdata.aX / 64;
                pX += pdata.kX;
            }

            BitmapData bData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

            Marshal.Copy(map, 0, bData.Scan0, map.Length);

            bmp.UnlockBits(bData);

            map = null;

            DateTime timeF = DateTime.Now;

            Console.WriteLine("Time in mapping them pixels: {0} milliseconds.", (timeF- timeS).TotalMilliseconds);

            GC.Collect();

            ClientSize      = new System.Drawing.Size(wX, wY);
            StartPosition   = FormStartPosition.CenterScreen;

            BackColor       = Color.White;

            Invalidate();
        }