Пример #1
0
        /// <summary>
        /// Renders the <see cref="Duality.Resources.Font"/> using the specified system font.
        /// This method assumes that the system font's size and style match the one specified in
        /// the specified Duality font.
        /// </summary>
        private RenderedFontData RenderGlyphs(SysDrawFont internalFont, FontCharSet charSet, bool antialiazing, bool monospace)
        {
            DualityFont.GlyphData[] glyphs = new DualityFont.GlyphData[charSet.Chars.Length];
            for (int i = 0; i < glyphs.Length; i++)
            {
                glyphs[i].Glyph = charSet.Chars[i];
            }

            int bodyAscent = 0;
            int baseLine = 0;
            int descent = 0;
            int ascent = 0;

            TextRenderingHint textRenderingHint;
            if (antialiazing)
                textRenderingHint = TextRenderingHint.AntiAliasGridFit;
            else
                textRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;

            int cols;
            int rows;
            cols = rows = (int)Math.Ceiling(Math.Sqrt(glyphs.Length));

            PixelData pixelLayer = new PixelData(
                MathF.RoundToInt(cols * internalFont.Size * 1.2f),
                MathF.RoundToInt(rows * internalFont.Height * 1.2f),
                ColorRgba.TransparentBlack);
            PixelData glyphTemp;
            PixelData glyphTempTypo;
            Bitmap bm;
            Bitmap measureBm = new Bitmap(1, 1);
            Rect[] atlas = new Rect[glyphs.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 < glyphs.Length; ++i)
                {
                    string str = glyphs[i].Glyph.ToString(CultureInfo.InvariantCulture);
                    bool isSpace = str == " ";
                    SizeF charSize = measureGraphics.MeasureString(str, internalFont, pixelLayer.Width, formatDef);

                    // Rasterize a single glyph for rendering
                    bm = new Bitmap((int)Math.Ceiling(Math.Max(1, charSize.Width)), internalFont.Height + 1);
                    using (Graphics glyphGraphics = Graphics.FromImage(bm))
                    {
                        glyphGraphics.Clear(Color.Transparent);
                        glyphGraphics.TextRenderingHint = textRenderingHint;
                        glyphGraphics.DrawString(str, internalFont, fntBrush, new RectangleF(0, 0, bm.Width, bm.Height), formatDef);
                    }
                    glyphTemp = new PixelData();
                    glyphTemp.FromBitmap(bm);

                    // Rasterize a single glyph in typographic mode for metric analysis
                    if (!isSpace)
                    {
                        Point2 glyphTempOpaqueTopLeft;
                        Point2 glyphTempOpaqueSize;
                        glyphTemp.GetOpaqueBoundaries(out glyphTempOpaqueTopLeft, out glyphTempOpaqueSize);

                        glyphTemp.SubImage(glyphTempOpaqueTopLeft.X, 0, glyphTempOpaqueSize.X, glyphTemp.Height);

                        if (charSet.CharBodyAscentRef.Contains(glyphs[i].Glyph))
                            bodyAscent += glyphTempOpaqueSize.Y;
                        if (charSet.CharBaseLineRef.Contains(glyphs[i].Glyph))
                            baseLine += glyphTempOpaqueTopLeft.Y + glyphTempOpaqueSize.Y;
                        if (charSet.CharDescentRef.Contains(glyphs[i].Glyph))
                            descent += glyphTempOpaqueTopLeft.Y + glyphTempOpaqueSize.Y;

                        bm = new Bitmap((int)Math.Ceiling(Math.Max(1, charSize.Width)), internalFont.Height + 1);
                        using (Graphics glyphGraphics = Graphics.FromImage(bm))
                        {
                            glyphGraphics.Clear(Color.Transparent);
                            glyphGraphics.TextRenderingHint = textRenderingHint;
                            glyphGraphics.DrawString(str, internalFont, fntBrush, new RectangleF(0, 0, bm.Width, bm.Height), formatTypo);
                        }
                        glyphTempTypo = new PixelData();
                        glyphTempTypo.FromBitmap(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 += internalFont.Height + MathF.Clamp((int)MathF.Ceiling(internalFont.Height * 0.1875f), 3, 10);
                    }

                    // Memorize atlas coordinates & glyph data
                    glyphs[i].Width = glyphTemp.Width;
                    glyphs[i].Height = glyphTemp.Height;
                    glyphs[i].OffsetX = glyphTemp.Width - glyphTempTypo.Width;
                    glyphs[i].OffsetY = 0; // ttf fonts are rendered on blocks that are the whole size of the height - so no need for offset
                    if (isSpace)
                    {
                        glyphs[i].Width /= 2;
                        glyphs[i].OffsetX /= 2;
                    }
                    atlas[i].X = x;
                    atlas[i].Y = y;
                    atlas[i].W = glyphTemp.Width;
                    atlas[i].H = (internalFont.Height + 1);

                    // Draw it onto the font surface
                    glyphTemp.DrawOnto(pixelLayer, BlendMode.Solid, x, y);

                    x += glyphTemp.Width + MathF.Clamp((int)MathF.Ceiling(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;
            }

            // Monospace offset adjustments
            if (monospace)
            {
                int maxGlyphWidth = 0;
                for (int i = 0; i < glyphs.Length; i++)
                {
                    maxGlyphWidth = Math.Max(maxGlyphWidth, glyphs[i].Width);
                }
                for (int i = 0; i < glyphs.Length; ++i)
                {
                    glyphs[i].OffsetX -= (int)Math.Round((maxGlyphWidth - glyphs[i].Width) / 2.0f);
                }
            }

            // Determine Font properties
            {
                float lineSpacing = internalFont.FontFamily.GetLineSpacing(internalFont.Style);
                float emHeight = internalFont.FontFamily.GetEmHeight(internalFont.Style);
                float cellAscent = internalFont.FontFamily.GetCellAscent(internalFont.Style);
                float cellDescent = internalFont.FontFamily.GetCellDescent(internalFont.Style);

                ascent = (int)Math.Round(cellAscent * internalFont.Size / emHeight);
                bodyAscent /= charSet.CharBodyAscentRef.Length;
                baseLine /= charSet.CharBaseLineRef.Length;
                descent = (int)Math.Round(((float)descent / charSet.CharDescentRef.Length) - (float)baseLine);
            }

            // Aggregate rendered and generated data into our return value
            FontMetrics metrics = new FontMetrics(
                size:       internalFont.SizeInPoints,
                height:     (int)internalFont.Height,
                ascent:     ascent,
                bodyAscent: bodyAscent,
                descent:    descent,
                baseLine:   baseLine,
                monospace:  monospace);
            return new RenderedFontData
            {
                Bitmap = pixelLayer,
                Atlas = atlas,
                GlyphData = glyphs,
                Metrics = metrics
            };
        }
Пример #2
0
        /// <summary>
        /// Generates pixel and atlas data for a single <see cref="Tileset"/> visual layer.
        /// </summary>
        /// <param name="renderInput"></param>
        /// <param name="sourceData"></param>
        /// <param name="geometry"></param>
        /// <param name="tileData"></param>
        /// <returns></returns>
        private LayerPixelData GenerateLayerPixelData(TilesetRenderInput renderInput, PixelData sourceData, LayerGeometry geometry, RawList<TileInfo> tileData)
        {
            // Create a buffer for writing target pixel data
            LayerPixelData target;
            target.PixelData = new PixelData(geometry.TargetTextureSize.X, geometry.TargetTextureSize.Y);
            target.Atlas = new List<Rect>();

            // Iterate over tiles and move each tile from source to target
            Point2 targetTilePos = new Point2(0, 0);
            for (int tileIndex = 0; tileIndex < geometry.SourceTileCount; tileIndex++)
            {
                // Initialize a new tile info when necessary
                if (tileIndex >= tileData.Count)
                {
                    tileData.Count++;
                    tileData.Data[tileIndex].IsVisuallyEmpty = true;
                }

                // Determine where on the source buffer the tile is located
                Point2 sourceTilePos = new Point2(
                    geometry.SourceTileAdvance.X * (tileIndex % geometry.SourceTilesPerRow),
                    geometry.SourceTileAdvance.Y * (tileIndex / geometry.SourceTilesPerRow));

                // Draw the source tile onto the target buffer, including its spacing / border
                Point2 targetContentPos = new Point2(
                    targetTilePos.X + renderInput.TargetTileMargin,
                    targetTilePos.Y + renderInput.TargetTileMargin);
                sourceData.DrawOnto(target.PixelData,
                    BlendMode.Solid,
                    targetContentPos.X,
                    targetContentPos.Y,
                    renderInput.SourceTileSize.X,
                    renderInput.SourceTileSize.Y,
                    sourceTilePos.X,
                    sourceTilePos.Y);

                // Fill up the target spacing area with similar pixels
                if (renderInput.TargetTileMargin > 0)
                {
                    FillTileSpacing(target.PixelData, renderInput.TargetTileMargin, targetContentPos, renderInput.SourceTileSize);
                }

                // Update whether the tile is considered visually empty
                if (tileData.Data[tileIndex].IsVisuallyEmpty)
                {
                    bool isLayerVisuallyEmpty = IsCompletelyTransparent(
                        sourceData,
                        sourceTilePos,
                        renderInput.SourceTileSize);
                    if (!isLayerVisuallyEmpty)
                        tileData.Data[tileIndex].IsVisuallyEmpty = false;
                }

                // Add an entry to the generated atlas
                Rect atlasRect = new Rect(
                    targetTilePos.X + renderInput.TargetTileMargin,
                    targetTilePos.Y + renderInput.TargetTileMargin,
                    geometry.TargetTileAdvance.X - renderInput.TargetTileMargin * 2,
                    geometry.TargetTileAdvance.Y - renderInput.TargetTileMargin * 2);
                target.Atlas.Add(atlasRect);

                // Advance the target tile position
                targetTilePos.X += geometry.TargetTileAdvance.X;
                if (targetTilePos.X + geometry.TargetTileAdvance.X > target.PixelData.Width)
                {
                    targetTilePos.X = 0;
                    targetTilePos.Y += geometry.TargetTileAdvance.Y;
                }
            }

            return target;
        }
Пример #3
0
        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));

            PixelData pixelLayer = new PixelData(MathF.RoundToInt(cols * this.internalFont.Size * 1.2f), MathF.RoundToInt(rows * this.internalFont.Height * 1.2f));
            PixelData glyphTemp;
            PixelData 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 PixelData(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 (CharBodyAscentRef.Contains(SupportedChars[i]))
                            this.bodyAscent += glyphTempBounds.Height;
                        if (CharBaseLineRef.Contains(SupportedChars[i]))
                            this.baseLine += glyphTempBounds.Bottom;
                        if (CharDescentRef.Contains(SupportedChars[i]))
                            this.descent += glyphTempBounds.Bottom;

                        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 PixelData(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
            {
                float lineSpacing = this.internalFont.FontFamily.GetLineSpacing(this.internalFont.Style);
                float emHeight = this.internalFont.FontFamily.GetEmHeight(this.internalFont.Style);
                float cellAscent = this.internalFont.FontFamily.GetCellAscent(this.internalFont.Style);
                float cellDescent = this.internalFont.FontFamily.GetCellDescent(this.internalFont.Style);
                float height = this.internalFont.GetHeight();

                this.height = this.internalFont.Height;
                this.ascent = (int)Math.Round(cellAscent * this.internalFont.Size / emHeight);
                this.bodyAscent /= CharBodyAscentRef.Length;
                this.baseLine /= CharBaseLineRef.Length;
                this.descent = (int)Math.Round(((float)this.descent / CharDescentRef.Length) - (float)this.baseLine);
                //this.descent = (int)Math.Round(cellDescent * height / lineSpacing);
                //this.baseLine = (int)Math.Round(cellAscent * height / lineSpacing);
            }

            // Create internal Pixmap and Texture Resources
            this.pixelData = new Pixmap(pixelLayer);
            this.pixelData.Atlas = new List<Rect>(atlas);
            this.texture = new Texture(this.pixelData,
                TextureSizeMode.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 * 4.0f);
            }
            this.mat = new Material(matInfo);
        }