2

I've run into an issue with drawing text and basic graphic drawing operations not having the proper placement & quality when the drawing matrix has large offset values. I've tried number SmoothMode, InterpolationMode, & TextRenderingHint options with no luck.

using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

public void RenderImageClosePointDrawing()
    {
        float x = 68336, y = 99460;            
        PointF anchorPoint = new PointF(17494176, 25461836);
        PointF anchorPoint2= new PointF(17494076, 25461836);
        string textLabel = "9318";
        float textFontSize = 20;
        float symbolsize = 34;
        string fontFamly = "Arial";

        Bitmap bitmap = new Bitmap(256, 256);
        using (Graphics graphics = Graphics.FromImage(bitmap))
        {
            graphics.SmoothingMode = SmoothingMode.HighQuality;                
            graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
            graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;

            graphics.Transform = new Matrix(1, 0, 0, 1, -x * 256, -y * 256);

            //Draw the circle
            Pen polyPen = new Pen(new SolidBrush(Color.Black), 2);
            Brush polyBrush = new SolidBrush(Color.Teal);
            graphics.DrawEllipse(polyPen, anchorPoint.X, anchorPoint.Y, symbolsize, symbolsize);
            graphics.FillEllipse(polyBrush, anchorPoint.X, anchorPoint.Y, symbolsize, symbolsize);
            RectangleF drawnArea = new RectangleF(anchorPoint.X, anchorPoint.Y, symbolsize, symbolsize);

            Pen polyPen2 = new Pen(new SolidBrush(Color.Black), 1);
            Brush polyBrush2 = new SolidBrush(Color.Teal);
            graphics.DrawEllipse(polyPen2, anchorPoint2.X, anchorPoint2.Y, symbolsize, symbolsize);
            graphics.FillEllipse(polyBrush2, anchorPoint2.X, anchorPoint2.Y, symbolsize, symbolsize);
            RectangleF drawnArea2 = new RectangleF(anchorPoint2.X, anchorPoint2.Y, symbolsize, symbolsize);

            Pen polyPen3 = new Pen(new SolidBrush(Color.Red), 1);
            graphics.DrawRectangle(polyPen3, drawnArea.X, drawnArea.Y, drawnArea.Width, drawnArea.Height);
            graphics.DrawRectangle(polyPen3, drawnArea2.X, drawnArea2.Y, drawnArea2.Width, drawnArea2.Height);

            //Draw the text
            Pen textOutlinePen = new Pen(new SolidBrush(Color.Orange), (float)4);
            textOutlinePen.EndCap = LineCap.Round;
            textOutlinePen.LineJoin = LineJoin.Round;
            textOutlinePen.MiterLimit = 0;
            Brush textFillBrush = new SolidBrush(Color.Teal);
            FontFamily textFontFamily = new FontFamily(fontFamly);

            PointF textAnchor = new PointF(anchorPoint.X, anchorPoint.Y);

            ShiftTextAnchor_NW(textLabel, textFontSize, ref drawnArea, textFontFamily, ref textAnchor);

            var textPath = new GraphicsPath();
            textPath.AddString(textLabel,
                            textFontFamily,
                            (int)FontStyle.Bold,
                            textFontSize,
                            textAnchor,
                            new StringFormat()
                        );
            graphics.DrawPath(textOutlinePen, textPath);
            graphics.FillPath(textFillBrush, textPath);



            //Draw the text2
            Pen textOutlinePen2 = new Pen(new SolidBrush(Color.Orange), (float)1);
            textOutlinePen.EndCap = LineCap.Round;
            textOutlinePen.LineJoin = LineJoin.Round;
            textOutlinePen.MiterLimit = 0;

            PointF textAnchor2 = new PointF(anchorPoint2.X, anchorPoint2.Y);

            ShiftTextAnchor_NW(textLabel, textFontSize, ref drawnArea2, textFontFamily, ref textAnchor2);

            var textPath2 = new GraphicsPath();
            textPath2.AddString(textLabel,
                            textFontFamily,
                            (int)FontStyle.Bold,
                            textFontSize,
                            textAnchor2,
                            new StringFormat()
                        );
            graphics.DrawPath(textOutlinePen2, textPath2);
            graphics.FillPath(textFillBrush, textPath2);

        }
        bitmap.Save(@"C:\ClosePointDrawing.png", ImageFormat.Png);
    }

private static void ShiftTextAnchor_NW(string textLabel, float textFontSize, ref RectangleF drawnArea, FontFamily textFontFamily, ref PointF textAnchor)
    {
        GraphicsPath tempPath = new GraphicsPath();
        tempPath.AddString(
                textLabel,
                textFontFamily,
                (int)FontStyle.Bold,
                textFontSize,
                textAnchor,
                new StringFormat()
            );
        var textBounds = tempPath.GetBounds();
        var offsetX = textBounds.X - textAnchor.X;
        var offsetY = textBounds.Y - textAnchor.Y;

        textAnchor = new PointF(drawnArea.Left - (textBounds.Width + offsetX), drawnArea.Top - (textBounds.Height + offsetY));
    }      

When you run this code you'll get this for output: ClosePointDrawing.png

You'll notice that the text doesn't not look nice (distorted some) and also that the Black stroke outline around the circle is only lined up properly with its Teal color filled circle with the one on the left when using a 1 pixel stroke. The one on the right uses a 2 pixel stroke. You'll also see that the Red Squares are not lined up with the Teal Circle, they should be completely encapsulating it.

Now if you change the first few values in the code so that it doesn't use a large offset as follows: float x = 0, y = 0; PointF anchorPoint = new PointF(150, 50); PointF anchorPoint2 = new PointF(50, 50);

You'll get this for output: ClosePointDrawing2.png Notice that the Text looks much better, and that the strokes are perfectly lined up with the filled circles as well as the red squares.

Is there anything that can be done, so that it will render it properly with the larger matrix?

  • I wonder if GDI+ can handle such large values and amazed it produced any results at all.Why do you want to do such a thing and onto a 256x256 bitmap, too??? – TaW Oct 06 '16 at 17:26
  • For handling Google map tile coordinates at closest zoom level. So is the only way to handle this is to do the offset adjustment myself instead of with the Transformation matrix? – Jered Berge Oct 06 '16 at 17:37
  • 1
    GDI+ uses 32-bit floating point values for internal math. Same as *float* in a C# program. Precision is rather limited, 6 significant digits max when you do any math. Note how 17494176 is already well beyond that, the round-off error gets stuff out of whack. You'll have to write smarter code. – Hans Passant Oct 06 '16 at 17:51

0 Answers0