Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
System.Drawing - Umschließendes Rechteck von rotiertem Text ermitteln
Waschbecken
myCSharp.de - Member



Dabei seit:
Beiträge: 807

Themenstarter:

System.Drawing - Umschließendes Rechteck von rotiertem Text ermitteln

beantworten | zitieren | melden

Hallo zusammen,

ich erstelle Grafiken, die lediglich Text enthalten. Die Größe der Grafiken bestimmt sich dabei aus dem Text. Momentan lese ich also mittels Graphics.MeasureString() die Größe aus, erstelle das Bitmap und zeichne dann mittels Graphics.DrawString() den Text darauf. Soll der Text rotiert werden, nehme ich das fertige Bitmap und drehe es.

Das wäre an sich zwar sicher nicht der performanteste, aber doch ein gangbarer Weg. Mein Problem ist allerdings, dass ich eine begrenzte Farbpalette zur Verfügung habe und somit jegliches Antialiasing (was neue Farben erzeugt) vermeiden muss.

Deshalb verwende ich beim Rotieren InterpolationMode.NearestNeighbor. Das funktioniert zwar, erzeugt aber ein ziemlich bescheidenes Ergebnis. Daher mein Gedanke: sollte es nicht möglich sein den Text normal zu zeichnen und mittels Graphics.RotateTransform() zu drehen?

(Der Unterschied ist zwar gering aber doch deutlich sichtbar, ich habe Beides schon verglichen.)

Ich bekomme es momentan nicht hin ohne mit dem Text irgendwo über die Grenzen des Rechtecks zu laufen, d.h. Text abzuschneiden (ich muss die finale Größe des Bitmaps ja kennen und festlegen, bevor ich den Text zeichnen kann).

Hat jemand eine Idee, wie das zu lösen wäre?
private Nachricht | Beiträge des Benutzers
chilic
myCSharp.de - Experte



Dabei seit:
Beiträge: 2136

beantworten | zitieren | melden

Ich versteh die Frage ehrlich gesagt nicht ganz. Erklär am besten nochmal wo du welche Probleme hast.
Zitat
sollte es nicht möglich sein den Text normal zu zeichnen und mittels Graphics.RotateTransform() zu drehen?
Ich dachte das machst du bereits so? Oder meinst du in deinem Satz vorher das komplette Bild, statt nur den einen Text? Es müsste doch auch gehen, den Text gedreht in ein Bild zu zeichnen und das dann mit transparentem Hintergrund in dein endgültiges Bild zu setzen.
Zitat
Ich bekomme es momentan nicht hin ohne mit dem Text irgendwo über die Grenzen des Rechtecks zu laufen, d.h. Text abzuschneiden (ich muss die finale Größe des Bitmaps ja kennen und festlegen, bevor ich den Text zeichnen kann).
Das ist dann ja wieder eine ganz andere Frage, da müsstest du mit Trigonometrie die wirklich benötigte Größe des Bilds ausrechnen.
private Nachricht | Beiträge des Benutzers
Waschbecken
myCSharp.de - Member



Dabei seit:
Beiträge: 807

Themenstarter:

beantworten | zitieren | melden

Zitat von chilic
Erklär am besten nochmal wo du welche Probleme hast.

Vorgehensweise jetzt:
  1. Ich messe die Größe des Strings aus.
  2. Ich erstelle mit der Größe ein Bitmap.
  3. Ich zeichne den Text auf das Bitmap.
  4. Wenn der Text rotiert werden soll, rotiere ich das gesamte bereits erzeugte Bitmap.


Das Problem: die Qualität des Textes ist schlecht, da ich zur Verwendung von InterpolationMode.NearestNeighbor gezwungen bin, da ich kein Antialiasing zulassen darf.

Daher der Gedanke das Ganze umzustellen und den Text gleich gedreht zu zeichnen. Das Problem: ich muss vorher bereits die Größe des finalen Bitmaps bekommen, auf das dann gezeichnet wird.
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 52329
Herkunft: Berlin

beantworten | zitieren | melden

Hallo Waschbecken,
Zitat
Ich bekomme es momentan nicht hin ohne mit dem Text irgendwo über die Grenzen des Rechtecks zu laufen
zwar kann man sicher per Trigonometrie ausrechnen, wieviel Platz das Rechteck, das den Text einschließt, brauchen würde, wenn man es dreht. Aber das sagt ja nichts darüber aus, wieviel Platz der eigentliche Text nach dem Drehen braucht, weil das von der genauen Form insbesondere des ersten und des letzten Buchstaben abhängt. Und das ist ja wohl genau das Problem.
Zitat
ich muss vorher bereits die Größe des finalen Bitmaps bekommen, auf das dann gezeichnet wird.
Diese Annahme würde ich in Frage stellen. Es ist nicht nötig, die finale Größe von Anfang an zu kennen. Du kannst es so machen, wie ich es in MeasureString und Alternativen [Minimales einschließendes Rechteck der Schrift ermitteln] beschreiben habe. Dass der Text bei dir zusätzlich noch gedreht ist, ändert ja nichts am Prinzip.

herbivore
private Nachricht | Beiträge des Benutzers
Waschbecken
myCSharp.de - Member



Dabei seit:
Beiträge: 807

Themenstarter:

beantworten | zitieren | melden

Hallo herbivore,

mein Problem ist allerdings tatsächlich herauszufinden, wie groß das Rechteck ist, in dem sich der Text befindet, nachdem er gedreht wurde. Siehe Anhang - ich benötige die Größe für B, nicht für A (A bekomme ich ja z.B. auf deinem Weg).
Attachments
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 52329
Herkunft: Berlin

beantworten | zitieren | melden

Hallo Waschbecken,

wenn du mit A und B tatsächlich genau das rote und blaue Rechteck meinst und deren Verhältnis zueinander so ist, wie grafisch dargestellt, also dass die Ecken von A die Kanten von B berühren sollen, dann kann man aus A durch einfache Trigonometrie B ausrechnen. Da sehe ich kein Problem. Man kann muss nur auf die Dreiecke achten, von denen man immer eine Kante und zwei Winkel kennt, also alle anderen Kanten (und Winkel) ausrechnen kann.

EDIT: Noch einfacher geht es, wenn man die Punkte des Rechtecks A mit Graphics.TransformPoints dreht und anschließend die minimalen und maximalen X- und Y-Koordinaten der gedrehten Punkte ermittelt.
Zitat
A bekomme ich ja z.B. auf deinem Weg
Meinen Weg kann man aber auch - natürlich etwas angepasst - verwenden, um das minimale umschließende Rechteck des gedrehten Textes auszurechnen. So hatte ich das gemeint, denn ich bin bisher davon ausgegangen, dass du genau das willst.

herbivore
private Nachricht | Beiträge des Benutzers
Waschbecken
myCSharp.de - Member



Dabei seit:
Beiträge: 807

Themenstarter:

beantworten | zitieren | melden

Ok, ich habe es gelöst (ob das nun "der" Weg nach Rom ist, sei mal dahingestellt). Nachfolgend ein komplettes, ausführbares Beispiel (als ASP.NET MVC-Controller).

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);

        }

    }
private Nachricht | Beiträge des Benutzers