Ein Kampf, wenn Sie versuchen, dasselbe wie Javas XOR-Modus in C # zu tun

Einführung

Wir haben uns entschlossen, das vorhandene Java-Applet in C # im .Net Framework erneut zu implementieren. Das ursprüngliche Programm war eine Kellenzeichnungs-App, und nach verschiedenen Untersuchungen zur Portierung schien der XOR-Modus der größte Engpass zu sein. Notieren Sie sich, was ich getan habe, um dies in C # zu erreichen.

Was ist der XOR-Modus?

Informationen zu XORMode finden Sie unter "Was ist setXORMode in der Grafikklasse von Java" von Yahoo Chiebukuro. Sie wissen wahrscheinlich nicht, was es ist. Ich habe es auch nicht wirklich verstanden.

In einem solchen Fall beschränkt es sich auf das Verständnis des Zwecks. Angenommen, Sie zeichnen eine Linie mit einem Zeichenprogramm, das eine Maus verwendet. Als Benutzeroperation sollten Sie mit der Maus an der Position klicken, an der Sie die Linie beginnen möchten, und die Maus an der Position anheben, an der Sie die Linie beenden möchten. In diesem Fall, egal ob Java oder C #, denke ich, dass die Implementierung wie folgt sein wird.

1 Notieren Sie die angeklickten Koordinaten (sagen wir x0, y0) im Ereignishandler des Mausklicks. 2 Zeichnen Sie eine Linie, die (x0, y0) mit den Koordinaten verbindet, an denen sich die Maus mit dem Ereignishandler der Maus bewegt hat. Dies ist ein Zustand, in dem der Benutzer die Maus bewegt, um die Endposition der Linie zu bestimmen, und eine temporäre Linie gezeichnet wird, da noch nicht festgelegt wurde, wo die Linie gezeichnet werden soll. (Hinweis) Zu diesem Zeitpunkt wird bei jeder Bewegung der Maus die während der vorherigen Mausbewegung gezeichnete Linie gelöscht. 3 Verwenden Sie den Mouse-Up-Ereignishandler, um die Mouse-Up-Koordinaten (z. B. x1, y1) zu ermitteln und eine Verbindungslinie zwischen (x0, y0) und (x1, y1) zu zeichnen. Dies bestätigt das Zeichnen der Linie.

Wenn es hier keinen in (Hinweis) beschriebenen "Prozess zum Löschen der Linie zum Zeitpunkt der vorherigen Mausbewegung" gibt, wird die Linie bei jeder Bewegung der Maus wie unten gezeigt erhöht. Werden.

image.png

Was soll ich nun mit "Löschen der während der letzten Mausbewegung gezeichneten Linie" tun? Sie könnten denken, dass Sie dieselbe Zeile wie beim letzten Mal mit einer anderen Farbe und einer anderen Hintergrundfarbe (in diesem Fall Weiß) überschreiben können. Sicher, das löscht die vorherige Linie, aber es überschreibt das, was ursprünglich auf dem Grafikobjekt gezeichnet wurde, mit einer weißen Linie, bevor die Linie gezeichnet wird. Nachfolgend sind einige Linien aufgeführt, die auf diese Weise gezeichnet wurden. Sie können sehen, dass die in der Vergangenheit gezeichneten Linien schwach sind.

image.png

** Hier kommt der XOR-Modus ins Spiel. Wenn Sie im XOR-Modus zweimal mit demselben Inhalt (derselben Farbe, derselben Position, derselben Form usw.) zeichnen, wird der ursprüngliche Zustand vollständig wiederhergestellt, einschließlich des Inhalts, den Sie vor dem Zeichnen der Linie gezeichnet haben. Es wird auch "umgekehrtes Zeichnen" genannt. ** ** **

Mit anderen Worten, es kann gesagt werden, dass es eine unverzichtbare Technik zum Zeichnen von sich bewegenden Objekten durch Bewegen der Maus, Ziehen der Maus usw. ist (ich wusste es vorher nicht (lacht)).

Wenn ich mir das ursprüngliche Java-Programm anschaue, wird der Vorgang des Überschreibens des zuvor gezeichneten Objekts im XOR-Modus jedes Mal wiederholt, wenn ich ein sich bewegendes Objekt zeichne.

Nachdem Sie eine allgemeine Vorstellung davon haben, was XORMode ist, schreiben wir einen Datensatz, den wir in C # nur schwer erreichen konnten.

Umgebung

Dieses Mal haben wir in der folgenden Umgebung untersucht.

Erstens nur mit der Standard-API von .Net Framework

Als ich mit .Net Framework nach XORMode suchte, fand ich zunächst Anzeigefarbe umkehren und Linie zeichnen. "Mit der DrawReversibleLine-Methode der ControlPaint-Klasse können Sie die Anzeigefarbe invertieren und eine Linie zeichnen. Sie können auch einen Rahmen mit der DrawReversibleFrame-Methode der ControlPaint-Klasse und ein Quadrat mit der FillReversibleRectangle-Methode zeichnen. . "es ist so geschrieben.

Basierend darauf habe ich es wie folgt implementiert. Da es von der Benutzersteuerung zum Verschieben implementiert wird, muss es in das Formular eingefügt werden. Darüber hinaus ist es erforderlich, "UserControl1_MouseDown" mit dem MouseDown-Ereignis, "UserControl1_MouseMove" mit dem MouseMove-Ereignis und "UserControl1_MouseUp" mit dem MouseUp-Ereignis in Visual Studio zu verknüpfen.

In dieser Quelle wird "ControlPaint.DrawReversibleLine" zum Zeichnen einer Linie und "ControlPaint.FillReversibleRectangle" zum Zeichnen eines gefüllten Quadrats verwendet. In beiden Fällen ist der Punkt, dass während MouseMove die Verarbeitung zweimal beschrieben wird, eine zum Löschen der vorherigen Zeichnung und die andere für diese Zeichnung.

UserControl1.cs


using System;
using System.Drawing;
using System.Windows.Forms;


namespace EditorSample
{
    public partial class UserControl1 : UserControl
    {

        /**
        *Zeichnungsfarbe
        */
        Color foreC = Color.Black;
        /**
        *Hintergrundzeichnung Farbe
        */
        Color backC = Color.White;

        //Zeichnen Sie Mausereignisse auf
        int x0, y0, x1, y1;

        enum Status
        {
            None,
            Draw,
        }

        Status status = Status.None;

        public UserControl1()
        {
            InitializeComponent();
        }

        private void UserControl1_Load(object sender, EventArgs e)
        {
            x0 = y0 = x1 = y1 = -1;
        }

        private void UserControl1_MouseDown(object sender, MouseEventArgs e)
        {
            Console.WriteLine("mouseDown" + "(" + e.X + "," + e.Y);	

            //Klickposition aufzeichnen
            x0 = x1 = e.X;
            y0 = y1 = e.Y;
            status = Status.Draw;
        }

        private void UserControl1_MouseMove(object sender, MouseEventArgs e)
        {
            Console.WriteLine("mouseMove" + "(" + e.X + "," + e.Y);
            Graphics g = CreateGraphics();

            //-----------------------------
            //Bewegung
            //-----------------------------

            /*Quadrat.Netto-Standardmethode*/
            if (x1 > 0)
            {
                ControlPaint.FillReversibleRectangle(new Rectangle(this.PointToScreen(new Point(x1, y1)), new Size(10, 10)), foreC);
            }
            ControlPaint.FillReversibleRectangle(new Rectangle(this.PointToScreen(new Point(e.X, e.Y)), new Size(10, 10)), foreC);

            //-----------------------------
            //Zeichnung
            //-----------------------------
            if (status == Status.Draw)
            {
                Console.WriteLine("mouseDrug" + "(" + e.X + "," + e.Y);
                Pen pen = new Pen(foreC);

                ControlPaint.DrawReversibleLine(this.PointToScreen(new Point(x1, y1)), this.PointToScreen(new Point(x0, y0)), foreC);
                ControlPaint.DrawReversibleLine(this.PointToScreen(new Point(e.X, e.Y)), this.PointToScreen(new Point(x0, y0)), foreC);
            }

            //Aktuelle Position aufzeichnen
            x1 = e.X;
            y1 = e.Y;
        }

        private void UserControl1_MouseUp(object sender, MouseEventArgs e)
        {
            Console.WriteLine("mouseUp" + "(" + e.X + "," + e.Y);

            if (status == Status.Draw)
            {
                Graphics g = CreateGraphics();
                Pen pen = new Pen(foreC);
                g.DrawLine(pen, new Point(e.X, e.Y), new Point(x0, y0));
                status = Status.None;
            }
            
            ControlPaint.FillReversibleRectangle(new Rectangle(this.PointToScreen(new Point(e.X, e.Y)), new Size(10, 10)), foreC);
        }
    }
}

Nach der Bewegung der Maus werden sowohl Linien als auch Quadrate problemlos angezeigt. ControlPaint bietet jedoch keine Methoden für Kreise und Polygone. Da die diesmal zu portierende Quelle die drawOval-Methode zum Zeichnen eines Kreises und die drawPolygon-Methode zum Zeichnen eines Polygons im Java Graphics-Objekt enthält, stellte sich heraus, dass es nicht so realisiert werden kann, wie es ist.

Nächster Zug

Als Ergebnis weiterer Untersuchungen https://github.com/EWSoftware/ImageMaps/blob/master/Source/WinForms/UnsafeNativeMethods.cs /WinForms/UnsafeNativeMethods.cs) lieferte ein Beispiel für den Aufruf von gdi32.dll, um das umgekehrte Zeichnen mit einem Kreis oder Polygon zu realisieren.

Ich habe diese Quelle in das Projekt aufgenommen und wie folgt implementiert. Dann konnte ich Kreise und Polygone umkehren.

UserControl01.cs


using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using EWSoftware.ImageMaps;

namespace EditorSample
{
    public partial class UserControl1 : UserControl
    {

        /**
        *Zeichnungsfarbe
        */
        Color foreC = Color.Black;
        /**
        *Hintergrundzeichnung Farbe
        */
        Color backC = Color.White;

        //Zeichnen Sie Mausereignisse auf
        int x0, y0, x1, y1;

        enum Status
        {
            None,
            Draw,
        }

        Status status = Status.None;

        public UserControl1()
        {
            InitializeComponent();
        }

        private void UserControl1_Load(object sender, EventArgs e)
        {
            x0 = y0 = x1 = y1 = -1;
        }

        private void UserControl1_MouseDown(object sender, MouseEventArgs e)
        {
            Console.WriteLine("mouseDown" + "(" + e.X + "," + e.Y);	

            //Klickposition aufzeichnen
            x0 = x1 = e.X;
            y0 = y1 = e.Y;
            status = Status.Draw;
        }

        private void UserControl1_MouseMove(object sender, MouseEventArgs e)
        {
            Console.WriteLine("mouseMove" + "(" + e.X + "," + e.Y);
            Graphics g = CreateGraphics();

            //-----------------------------
            //Bewegung
            //-----------------------------

            /*Methode mit der Yen-Bibliothek*/
            if (x1 > 0)
            {
                UnsafeNativeMethods.DrawReversibleCircle(g, new Point(x1, y1), 10, new Point(0, 0));
            }
            UnsafeNativeMethods.DrawReversibleCircle(g, new Point(e.X, e.Y), 10, new Point(0, 0));


            /*Methode unter Verwendung der Polygonbibliothek*/
            if (x1 > 0) { 
                List<Point> list_old = new List<Point>();
                list_old.Add(new Point(x1, y1));
                list_old.Add(new Point(x1 + 20, y1 - 10));
                list_old.Add(new Point(x1 + 20, y1 + 10));
                UnsafeNativeMethods.DrawReversiblePolygon(g, list_old, true, new Point(0, 0));
            }
            List<Point> list_new = new List<Point>();
            list_new.Add(new Point(e.X, e.Y));
            list_new.Add(new Point(e.X+20, e.Y-10));
            list_new.Add(new Point(e.X+20, e.Y+10));
            UnsafeNativeMethods.DrawReversiblePolygon(g, list_new, true, new Point(0, 0));

            //-----------------------------
            //Zeichnung
            //-----------------------------
            if (status == Status.Draw)
            {
                Console.WriteLine("mouseDrug" + "(" + e.X + "," + e.Y);
                Pen pen = new Pen(foreC);

                ControlPaint.DrawReversibleLine(this.PointToScreen(new Point(x1, y1)), this.PointToScreen(new Point(x0, y0)), foreC);
                ControlPaint.DrawReversibleLine(this.PointToScreen(new Point(e.X, e.Y)), this.PointToScreen(new Point(x0, y0)), foreC);
            }

            //Aktuelle Position aufzeichnen
            x1 = e.X;
            y1 = e.Y;
    }

        private void UserControl1_MouseUp(object sender, MouseEventArgs e)
        {
            Console.WriteLine("mouseUp" + "(" + e.X + "," + e.Y);

            if (status == Status.Draw)
            {
                Graphics g = CreateGraphics();
                Pen pen = new Pen(foreC);
                g.DrawLine(pen, new Point(e.X, e.Y), new Point(x0, y0));
                status = Status.None;
            }

        }
    }
}

Diese Bibliothek ist jedoch nicht gefüllt. Nun, ich brauchte es nicht, also implementiere ich es einfach nicht. Ich habe jedoch keine andere Wahl, als mich an diese Bibliothek zu halten, also muss ich es schaffen, sie zu füllen. Ich suchte weiterhin bei Google mit so vielen Keywords, wie ich mir vorstellen konnte.

Vorerst der Yen

Ich erinnere mich nicht, wie ich danach gesucht habe, aber für den Rush-Kreis lautet die Farbe `IntPtr oldBrush = SelectObject (hDC, GetStockObject (NULL_BRUSH));` in der `` `UnsafeNativeMethods.DrawReversibleCircle```-Methode der Bibliothek. Da es hier nicht angegeben ist

IntPtr oldBrush = SelectObject(hDC, CreateSolidBrush(ColorTranslator.ToWin32(backColor)));


 Es stellte sich heraus, dass wenn Sie es durch ersetzen, es mit der Farbe des von backColor angegebenen Color-Objekts gefüllt wird.

# Letztes schwieriges Polygon
 Die letzte Herausforderung ist die Polygonfüllung. Die Bibliothek `` `DrawReversiblePolygon``` zeichnete mehrmals Linie, um ein Polygon zu erstellen. Damit scheint es im Prinzip schwierig zu sein, den Inhalt zu füllen. Als ich erneut bei Google suchte, http://wisdom.sakura.ne.jp/system/winapi/win32/win29.html
 Es gibt eine native API von Don Pisha namens "Polygon", und es scheint, dass sie gefüllt werden kann, indem SetPolyFillMode gesetzt und aufgerufen wird. Also habe ich folgendes versucht.

 Zuerst habe ich die Quelle der Bibliothek geändert und eine Einstellung zum Importieren von Polygon und SetPolyFillMode hinzugefügt. Wir haben auch die Struktur der POINT-API definiert, um der Polygonfunktion polygonale Koordinaten zuzuweisen.

```csharp
        public struct POINTAPI
        {
            public int x;
            public int y;
        }

        [DllImport("gdi32.dll")]
        public static extern int Polygon(IntPtr hDC, ref POINTAPI lpPoint, int nCount);

        [DllImport("gdi32.dll")]C
        public static extern int SetPolyFillMode(IntPtr hdc, int nPolyFillMode);

Und ich habe die folgende FillReversiblePolygon-Methode implementiert. Die Punkte sind die Stelle, an der Sie anweisen, alle mit `SetPolyFillMode (hDC, 2);` zu füllen, und die Punkte in der .Net-Liste werden in das POINTAPI-Array und das Argument der Polygon-Funktion konvertiert. Ich setze es auf.

        internal static void FillReversiblePolygon(Graphics g, List<Point> points, Point offset, Color backColor)
        {

            IntPtr hDC = g.GetHdc();
            SetPolyFillMode(hDC, 2);

            IntPtr pen = CreatePen(PS_SOLID, 1, ColorTranslator.ToWin32(Color.Black));
            IntPtr brush = CreateSolidBrush(ColorTranslator.ToWin32(backColor));

            int oldROP = SetROP2(hDC, R2_NOTXORPEN);
            IntPtr oldBrush = SelectObject(hDC, brush);
            IntPtr oldPen = SelectObject(hDC, pen);

            SetBkColor(hDC, ColorTranslator.ToWin32(Color.White));


            POINTAPI[] pointsArray = new POINTAPI[points.Count];
            for (int i = 0; i < points.Count; i++)
            {
                pointsArray[i].x = points[i].X;
                pointsArray[i].y = points[i].Y;
            }
            Polygon(hDC, ref pointsArray[0], Enumerable.Count(points));

            SelectObject(hDC, oldPen);
            SelectObject(hDC, oldBrush);
            SetROP2(hDC, oldROP);
            DeleteObject(pen);
            DeleteObject(brush);
            g.ReleaseHdc(hDC);
        }

Jetzt ist das Set fertig. Nennen wir es von der Benutzersteuerung. Lassen Sie uns den Aufruf in der vorherigen Quelle wie folgt in "UnsafeNativeMethods.DrawReversiblePolygon" ändern. Farbe. Schwarz zeigt die Füllfarbe an.

            /*Methode unter Verwendung der Polygonbibliothek*/
            if (x1 > 0) { 
                List<Point> list_old = new List<Point>();
                list_old.Add(new Point(x1, y1));
                list_old.Add(new Point(x1 + 20, y1 - 10));
                list_old.Add(new Point(x1 + 20, y1 + 10));
                UnsafeNativeMethods.FillReversiblePolygon(g, list_old, new Point(e.X, 0), Color.Black);
            }

            List<Point> list_new = new List<Point>();
            list_new.Add(new Point(e.X, e.Y));
            list_new.Add(new Point(e.X+20, e.Y-10));
            list_new.Add(new Point(e.X+20, e.Y+10));
            UnsafeNativeMethods.FillReversiblePolygon(g, list_new, new Point(e.X, 0), Color.Black);

Dann wurde das Polygon auch problemlos angezeigt, nachdem die Maus in den Zustand versetzt wurde, in dem sie schwarz ausgefüllt war. Hmmm, es ist endlich vorbei.

abschließend

Es hat viel Zeit gekostet, eine technische Untersuchung der Anzeige von Objekten zwischen Mausbewegungen durchzuführen, was nicht der Hauptteil des Zeichenprozesses ist, aber darum geht es bei der Entwicklung.

Nachdem Ihre größte Sorge geklärt ist, nehmen wir das Hauptgericht der Portierung des Zeichenprozesses für verschiedene Objekte.

Nachtrag (2020/1/5)

In der UnsafeNativeMethods.DrawReversiblePolygon-Methode wurde das Löschen des von CreateSolidBrush mit DeleteObject erstellten Pinsels nicht berücksichtigt. Wenn Sie dies nicht tun, besteht die Gefahr, dass Ihnen die Grafikressourcen ausgehen, wenn Sie sie längere Zeit verwenden.

Verweise

Recommended Posts

Ein Kampf, wenn Sie versuchen, dasselbe wie Javas XOR-Modus in C # zu tun
Implementieren Sie dieselbe Funktion wie das C, C ++ - System ("cls") in Java
Was tun, wenn die Änderungen im Servlet nicht berücksichtigt werden?
Es sollte mit dem Beispiel identisch sein, wenn Sie sich bei Twitter anmelden, es tritt jedoch ein Fehler auf, bis die Lösung behoben ist
Das erste, was Sie tun müssen, wenn Sie mit Heroku auf GitHub mit Eclipse auf Java zufrieden sein möchten
Was tun, wenn das Präfix c in JSP nicht gebunden ist?
Hinweise zur Vorgehensweise beim Auftreten einer WebView ClassNotFoundException in JavaFX 12
Was tun, wenn der Wert im zweiten getSubmittedValue () in JSF Validator null wird?
Der Grund, warum Dunst bleibt, wenn versucht wird, die Objektorientierung als "Englisch" zu betrachten
Eine peinliche Geschichte, die am selben Tag behandelt wurde, als versucht wurde, die Daten 3/31 und 4/1 zu vergleichen [Java / Kalender]
Wenn Eclipse den Server nicht starten kann
Was tun, wenn in PlayFramework eine IllegalStateException auftritt?
[React.useRef] Was tun, wenn im Ereignis-Listener nicht auf den neuesten Status verwiesen werden kann?
Was ist zu tun, wenn in Eclipse "Fehler beim Laden der gemeinsam genutzten JNI-Bibliothek" angezeigt wird?