Как нарисовать повернутую строку как изображение с помощью System.Drawing? - PullRequest
7 голосов
/ 05 апреля 2011

Я рисую строки для изображений. Размер изображений является динамическим, или, другими словами, изображения настолько велики, насколько это необходимо для отображения строк. Чтобы добиться этого, я измеряю размер с помощью Graphics.MeasureString (), прежде чем визуализировать текст с помощью Graphics.DrawString ().

Это все нормально до момента, когда вращение вступает в игру. До сих пор я рисую строку в растровом изображении и вращаю все растровое изображение, которое получается в результате.

Проблема в том, что у меня очень ограниченная цветовая палитра и нет смешанных цветов. Поэтому я должен избегать любого вида сглаживания, что возможно только при использовании InterpolationMode.NearestNeighbor путем вращения растрового изображения текста. Хотя это гарантирует, что нежелательный цвет не будет отображен, результат действительно очень уродлив (с точки зрения пользователя).

Моя мысль: должна быть возможность нарисовать текст в растровом изображении, вращая его с помощью Graphics.RotateTransform () и избегая отсечения, не так ли?

Поскольку мне нужно сначала определить размер изображения для рисования, а этот размер увеличивается при вращении, я понятия не имею, как это сделать.

Любая помощь очень ценится!

Ответы [ 2 ]

4 голосов
/ 05 апреля 2011

Этот код даст вам представление:

    public void DrawText(bool debug, Graphics g, string text, Font font, Brush brush, StringFormat format, float x, float y, float width, float height, float rotation)
    {
        float centerX = width / 2;
        float centerY = height / 2;

        if (debug)
        {
            g.FillEllipse(Brushes.Green, centerX - 5f, centerY - 5f, 10f, 10f);
        }

        GraphicsState gs = g.Save();

        Matrix mat = new Matrix();
        mat.RotateAt(rotation, new PointF(centerX, centerY), MatrixOrder.Append);

        g.Transform = mat;

        SizeF szf = g.MeasureString(text, font);

        g.DrawString(text, font, brush, (width / 2) - (szf.Width / 2), (height / 2) - (szf.Height / 2), format);

        g.Restore(gs);
    }

Вот метод измерения границ повернутого текста с использованием GraphicsPath.Логика проста, GraphicsPath преобразует текст в список точек, а затем вычисляет границы прямоугольника.

    public RectangleF GetRotatedTextBounds(string text, Font font, StringFormat format, float rotation, float dpiY)
    {
        GraphicsPath gp = new GraphicsPath();

        float emSize = dpiY * font.Size / 72;

        gp.AddString(text, font.FontFamily, (int)font.Style, emSize, new PointF(0, 0), format);

        Matrix mat = new Matrix();
        mat.Rotate(rotation, MatrixOrder.Append);

        gp.Transform(mat);

        return gp.GetBounds();
    }

Тестовый код:

        float fontSize = 25f;
        float rotation = 30f;

        RectangleF txBounds = GetRotatedTextBounds("TEST TEXT", new Font("Verdana", fontSize, System.Drawing.FontStyle.Bold), StringFormat.GenericDefault, rotation, 96f);

        float inflateValue = 10 * (fontSize / 100f);

        txBounds.Inflate(inflateValue, inflateValue);

        Bitmap bmp = new System.Drawing.Bitmap((int)txBounds.Width, (int)txBounds.Height);
        using (Graphics gr = Graphics.FromImage(bmp))
        {
            gr.Clear(Color.White);
            DrawText(true, gr, "TEST TEXT", new Font("Verdana", fontSize, System.Drawing.FontStyle.Bold), Brushes.Red, new StringFormat(System.Drawing.StringFormatFlags.DisplayFormatControl), 0, 0, txBounds.Width, txBounds.Height, rotation);
        }
1 голос
/ 05 апреля 2011

Мое решение как исполняемое действие MVC:

public class ImageController : Controller
{

    public ActionResult Test()
    {

        var text = DateTime.Now.ToString();
        var font = new Font("Arial", 20, FontStyle.Regular);
        var angle = 233;

        SizeF textSize = GetEvenTextImageSize(text, font);

        SizeF imageSize;

        if (angle == 0)
            imageSize = textSize;
        else
            imageSize = GetRotatedTextImageSize(textSize, angle);

        using (var canvas = new Bitmap((int)imageSize.Width, (int)imageSize.Height))
        {

            using(var graphics = Graphics.FromImage(canvas))
            {

                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                graphics.SmoothingMode = SmoothingMode.HighQuality;
                graphics.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;

                SizeF textContainerSize = graphics.VisibleClipBounds.Size;
                graphics.TranslateTransform(textContainerSize.Width / 2, textContainerSize.Height / 2);
                graphics.RotateTransform(angle);

                graphics.DrawString(text, font, Brushes.Black, -(textSize.Width / 2), -(textSize.Height / 2));

            }

            var stream = new MemoryStream();
            canvas.Save(stream, ImageFormat.Png);
            stream.Seek(0, SeekOrigin.Begin);
            return new FileStreamResult(stream, "image/png");

        }

    }

    private static SizeF GetEvenTextImageSize(string text, Font font)
    {
        using (var image = new Bitmap(1, 1, PixelFormat.Format32bppArgb))
        {
            using (Graphics graphics = Graphics.FromImage(image))
            {
                return graphics.MeasureString(text, font);
            }
        }
    }

    private static SizeF GetRotatedTextImageSize(SizeF fontSize, int angle)
    {

        // Source: http://www.codeproject.com/KB/graphics/rotateimage.aspx

        double theta = angle * Math.PI / 180.0;

        while (theta < 0.0)
            theta += 2 * Math.PI;

        double adjacentTop, oppositeTop;
        double adjacentBottom, oppositeBottom;

        if ((theta >= 0.0 && theta < Math.PI / 2.0) || (theta >= Math.PI && theta < (Math.PI + (Math.PI / 2.0))))
        {
            adjacentTop = Math.Abs(Math.Cos(theta)) * fontSize.Width;
            oppositeTop = Math.Abs(Math.Sin(theta)) * fontSize.Width;
            adjacentBottom = Math.Abs(Math.Cos(theta)) * fontSize.Height;
            oppositeBottom = Math.Abs(Math.Sin(theta)) * fontSize.Height;
        }
        else
        {
            adjacentTop = Math.Abs(Math.Sin(theta)) * fontSize.Height;
            oppositeTop = Math.Abs(Math.Cos(theta)) * fontSize.Height;
            adjacentBottom = Math.Abs(Math.Sin(theta)) * fontSize.Width;
            oppositeBottom = Math.Abs(Math.Cos(theta)) * fontSize.Width;
        }

        int nWidth = (int)Math.Ceiling(adjacentTop + oppositeBottom);
        int nHeight = (int)Math.Ceiling(adjacentBottom + oppositeTop);

        return new SizeF(nWidth, nHeight);

    }

}
...