Pinsel und Flächen

Bevor Flächen gezeichnet werden können, muss auch hier zuerst das entsprechende 'Werkzeug' ausgewählt werden. Flächen werden mit so genannten Pinsel (Brush) ausgefüllt und mit den in der vorherigen Lektionen eingeführten Stiften umrandet.

Wollen Sie Flächen ohne Umrandung zeichnen, so wählen Sie als Stift den Stifttyp NULL_PEN aus.

Und auch hier sind wieder am einfachsten die Pinsel zu verwenden, die WINDOWS von Haus aus zur Verfügung stellt. Diese Pinsel müssen ebenfalls nicht explizit erstellt werden sondern können durch die vorhin erwähnte Methode SelectStockObject(...) für die nachfolgende Zeichenoperation ausgewählt werden. Der Parameter gibt jetzt den auszuwählenden Pinsel an und kann eine der Konstanten BLACK_BRUSH, DKGRAY_BRUSH, GRAY_BRUSH, LTGRAY_BRUSH, HOLLOW_BRUSH, NULL_BRUSH oder WHITE_BRUSH sein. Die Pinsel HOLLOW_BRUSH und NULL_BRUSH füllen keine Flächen aus. Der nachfolgende Auszug aus einem Listing wählt zuerst einen schwarzen Pinsel und dann einen hellgrauen für nachfolgenden Zeichenoperationen aus:

void CMyView::OnDraw (CDC *pDC)
{
    pDC->SelectStockObject (BLACK_BRUSH);
    ....   
// Hier werden Flächen mit schwarzer Farbe ausgefuellt
    pDC->SelectStockObject (LTGRAY_BRUSH);
    ....   
// und hier mit hellgrauer Farbe
}

Und damit auch wieder etwas Farbe ins Spiel kommt, stehen Ihnen ebenfalls anwenderdefinierte Pinsel zur Verfügung. Soll ein anwenderdefinierter Pinsel erstellt werden, so kann dies wiederum auf zwei Arten erfolgen. Im ersten Fall wird ein MFC-Objekt vom Typ CBrush ohne Parameter erstellt und anschließend eine der Methoden CreateSolidBrush(...), CreateHatchBrush(...) oder CreatePatternBrush(...) aufgerufen. Die Methode CreateSolidBrush(...) erzeugt einen einfarbigen, nicht gemusterten Pinsel mit einer anzugebenden Farbe. CreateHatchBrush(...) erzeugt dagegen einen Pinsel mit einem vom System vorgegebenen Muster. Das Muster sowie die Farben werden über Parameter der Methode eingestellt. Für das Muster kann eine der Konstanten HS_BDIAGONAL, HS_CROSS, HS_DIAGCROSS, HS_FDIAGONAL, HS_HORIZONTAL oder HS_VERTICAL angegeben werden. Wird einer dieser gemusterten Pinsel verwendet, so wird der Hintergrund des Musters immer mit der aktuellen Hintergrundfarbe ausgefüllt (siehe auch Methode SetBkMode(...) und SetBkColor(...) in der vorherigen Lektion). Eine Sonderstellung nimmt die Methode CreatePatternBrush(...) ein. Sie erzeugt einen Pinsel über ein CBitmap-Objekt. Diese Methode wird immer dann verwendet, wenn das Muster des Pinsels frei einstellbar sein soll. Die Größe der als Brush zu verwendenden Bitmap ist unter WINDOWS98 und WINDOWS NT/2000 nicht begrenzt. Für WINDOWS95 sollte die Bitmap aber auf 8x8 Pixel begrenzt werden.

Brush-Methode

Brushdarstellung

Pinselstile

Alternativ kann ein Pinsel, genauso wie ein Stift, auch beim Aufruf seines Konstruktor bereits initialisiert werden.

Auch Pinsel werden nach ihrer Erstellung nicht automatisch für die nachfolgenden Zeichenoperationen verwendet. Sie müssen genauso wie Stifte mit der Methode SelectObject(...) explizit auswählt werden.

Sehen wir uns jetzt die Funktionen zum Zeichnen von Flächen an. Beginnen wir mit den Funktionen, die sich mit Rechtecken befassen. Die einfachste CDC-Methode zum Zeichnen eines Rechtecks ist die Methode Rectangle(...). Sie zeichnet ein Rechteck dessen Umrandung mit dem aktuellen Stift gezeichnet wird und das mit dem aktuellen Pinsel ausgefüllt wird. Die Koordinatenangaben für das Rechteck erfolgen wiederum in logischen Einheiten.

Die Koordinate des rechten, unteren Punktes gehört nicht mehr zum Rechteck! Dies gilt auch für alle nachfolgenden Rechteck-Funktionen. Außerdem muss die Breite und Höhe bei allen Rechteck-Methode unter WINDOWS95/98 kleiner 32,767 Einheiten betragen da dort ein 16-Bit Koordinatensystem verwendet wird.. WINDOWS NT/2000 hingegen verwendet ein 32-Bit Koordinatensystem, so dass dort die Grenze bei 2.147E+09 liegt.

Die nächste Methode zum Zeichnen eines Rechtecks ist die Methode FrameRect(...). Sie zeichnet nur einen Rahmen mit dem als Parameter übergebenen Pinsel. Die Breite des Rahmens ist immer 1 logische Einheit. Das Innere des Rechtecks wird nicht ausgefüllt.

Beachten Sie bitte bei dieser und der nachfolgenden Methode, dass folgende Bedingungen eingehalten werden: left<right und top<bottom. Werden diese Bedingungen nicht erfüllt, so wird das Rechteck nicht gezeichnet!

Die CDC-Methode FillRect(...) zeichnet ein ausgefülltes Rechteck, das mit einem anzugebenden Pinsel ausgefüllt wird. Eine Umrandung des Rechtecks erfolgt nicht.

Die letzte CDC-Methode zum Zeichnen eines Rechtecks ist die Methode RoundRect(...). Sie zeichnet ein Rechteck mit abgerundeten Ecken. Das Rechteck wird mit dem aktuellen Pinsel ausgefüllt und die Umrahmung wird mit dem aktuellen Stift gezeichnet.  Der letzte Parameter der Methode spezifiziert die Ellipse, die für die Rundungen an den Ecken verwendet wird (siehe nachfolgendes Bild).

Rechteck mit runden Ecken

Eine Sonderstellung bei den Rechteck-Methoden nimmt die CDC-Methode InvertRect(...) ein. Sie zeichnet kein Rechteck sondern invertiert alle Pixel die sich innerhalb des angegebenen Rechtecks befinden. Diese Invertierung ist eine boolsche NOT-Operation auf die innerhalb des Rechtecks liegenden Pixel. So erzeugt die Methode aus weißen Pixel (RGB(0xFF,0xFF,0xFF)) schwarze Pixel (RGB(0x00,0x00,0x00)). Das nachfolgende Bild veranschaulicht dies nochmals: Die Ellipse wurde mit einem grünen, schraffierten Pinsel (RGB(0x00,0xFF,0x00)) gezeichnet. Der Bereich der Ellipse, der sich innerhalb des zu invertierenden Rechtecks befindet, erhält als Ergebnis der Invertierung die Farbe lila (RGB(0xFF,0x00,0xFF)), d.h. alle RGB-Anteile der ursprünglichen Farbe erscheinen invertiert.

Invertiertes Rechteck

Eine zweimalige Invertierung des gleichen Bereichs stellt wieder den Urzustand des Bereichs her!

Außer Rechteck-Funktionen stellt das GDI, und damit auch die MFC-Klasse CDC, noch Funktionen zum Zeichnen von runden Flächen zur Verfügung. Um eine Ellipse zu zeichnen wird die CDC-Methode Ellipse(...) eingesetzt. Die übergebenen Koordinatenwerte, wieder in logischen Einheiten, spezifizieren das die Ellipse umschließende Rechteck. Die Ellipse wird ebenfalls mit dem aktuellen Pinsel ausgefüllt und die Umrandung mit dem aktuellen Stift gezeichnet.

WINDOWS kennt keine Funktion um einen Kreis zu zeichnen. Ein Kreis wird mit der Ellipsen-Funktion gezeichnet, wobei Breite gleich Höhe zu setzen ist.  Soll nur die Umrandung gezeichnet werden, d.h. ein nicht ausgefüllter Kreis, so ist über die Methode SelectStockObject(...) entweder der Pinsel NULL_BRUSH oder HOLLOW_BRUSH einzustellen.

Die CDC-Methode Pie(...) zeichnet einen Ellipsenausschnitt, d.h. ein 'Kuchenstück'. Dazu wird das die Ellipse umschließende Rechteck sowie der Start- und Endpunkt des  Kreissegments spezifiziert. Diese beide Punkte müssen nicht unmittelbar auf der Ellipse liegen. Die Standard-Zeichenrichtung ist auch hier gegen dem Uhrzeigersinn. Soll die Zeichenrichtung geändert werden, so ist hierfür die Methode SetArcDirection(...) aufzurufen.

Kreisausschnitt

Eine ähnliche Funktion besitzt die Methode Chord(...). Sie zeichnet ein Ellipsensegment. Ein Ellipsensegment verbindet den Start- und Endpunkt auf der Ellipse durch eine gerade Linie. Diese Methode besitzt die gleichen Parameter wie die Methode Pie(...).

Kreissegment

Die letzte Gruppe der Flächenfunktionen behandelt Polygone. Polygone sind ausgefüllte Linienzüge. Um Polygone darzustellen werden die CDC-Methoden Polygon(...) bzw. PolyPolygon(...) eingesetzt. Ihre Parameter entsprechen denen der Methode Polyline(...) bzw. PolyPolyline(...). Polygon(...) zeichnet einen ausgefüllten Linienzug mit den in einem CPoint-Feld abgelegten Stützpunkten. Der Linienzug wird automatisch vom letzten zum ersten Stützpunkt geschlossen. Das Polygon wird mit dem aktuellen Pinsel ausgefüllt und der Linienzug des Polygons wird mit dem aktuellen Stift gezeichnet. PolyPolygon(...) kann mit einem Aufruf mehrere solcher Polygone zeichnen.

Polygon

Im Zusammenhang mit Polygonen muss noch die CDC-Methode SetPolyFillMode(...) erwähnt werden. Über den Parameter der Methode kann der Modus eingestellt werden, der zum Ausfüllen des Polygons verwendet wird. Hierfür sind die beiden Werte WINDING und ALTERNATE zulässig. Wie sich der Füllmodus auswirkt kann dem nachfolgenden Bild entnommen werden. Hier wurde als Polygon ein einfacher Stern verwendet.

Füllmodi eines Polygons

Und damit beenden wir die Theorie. Jetzt sind Sie wieder gefragt!

Erweitern Sie das in der letzten Lektion begonnene Beispiel jetzt. Anstelle einer Polyline ist nun ein Polygon zu zeichnen. Das Polygon soll mit einem Pinsel ausgefüllt werden der ein diagonales Muster besitzt. Der Linienzug des Polygons ist weiterhin in rot zu zeichnen während das Füllmuster des Polygons mit grünen Streifen auf weißem Hintergrund gezeichnet werden soll.

Lösung zur Darstellung des Polygons

void CGDIFuncView::OnDraw(CDC* pDC)
{
    CGDIFuncDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    // ZU ERLEDIGEN: Hier Code zum Zeichnen der ursprünglichen Daten hinzufügen
    // Stift fuer das Koordinatensystem
    CPen    CAxisPen(PS_DASH,1,RGB(0,0,255));
    // Stift fuer die Kurve
    CPen    CLinePen;
    CLinePen.CreatePen(PS_SOLID,1,RGB(255,0,0));
    // Gemusterter Pinsel zum Ausfuellen des Polygons
    CBrush     CPFillBrush(HS_BDIAGONAL,RGB(0,255,0));
    ....
    // Messkurve zeichnen
    pDC->SelectObject(&CLinePen);
    // Fuellfarbe des Polygons setzen
    pDC->SelectObject(&CPFillBrush);
    pDC->SetBkColor(RGB(255,255,255));
    // Polygon zeichnen
    pDC->Polygon(m_pPts,m_nNoOfPts);
}

Zuerst muss wieder der für das Polygon zu verwendende Pinsel erstellt werden. Als Pinselstil wird für das diagonale Muster der Stil HS_BDIALGONAL verwendet. Ebenso hätten Sie hier das Muster HS_FDIAGONAL einsetzen können. Die Farbe des Muster wird beim Erstellen des Pinsels gleich auf grün gesetzt.

Dann wird vor dem Zeichnen des Polygons der neu erzeugte Pinsel ausgewählt und als Hintergrundfarbe für den Pinsel die Farbe weiß eingestellt. Danach kann das Polygon gezeichnet werden.

Ende der Lösung

Übersetzen und starten Sie das Programm. Wenn Sie ganz genau hinsehen können Sie sehen, dass der Polygonzug mit dem entsprechenden Pinsel ausgefüllt wurde.

Doch damit wollen wir es noch nicht bewenden lassen. Unser Fenster soll nun auch noch einen hellgrauen Hintergrund erhalten. Im ersten Ansatz könnten wir in der OnDraw(...) Methode diesen Hintergrund zeichnen. Doch wenn Sie sich ein klein wenig in den WINDOWS-Nachrichten umsehen werden Sie dort eine Nachricht mit der ID WM_ERASEBKGND finden. Diese Nachricht wird immer dann an ein Fenster gesendet, wenn dessen Hintergrund neu gezeichnet werden muss. Bauen wir jetzt den Nachrichtenbearbeiter für diese Nachricht in das Programm ein.

Fügen Sie über den Klassen-Assistenten den Nachrichtenbearbeiter für die WM_ERASEBKGND Nachricht zur Ansichtsklasse hinzu. Sie finden diese Nachricht unter dem Punkt Behandlungsroutine für Windows Nachrichten hinzufügen..... Modifizieren Sie die eingefügte Methode OnEraseBkgnd(...) wie folgt:
BOOL CGDIFuncView::OnEraseBkgnd(CDC* pDC)
{
    // TODO: Code für die Behandlungsroutine für Nachrichten hier einfügen ...
   
    // Groesse der Client-Area des Fensters holen
    CRect CClientRect;
    GetClientRect(&CClientRect);
    // Rechteck um eins in jeder Richtung erhoehen da die
    // rechte untere Kante nicht mitgezeichnet wird!
    CClientRect.bottom++;
    CClientRect.right++;
    // Rechteck nun ausfuellen
    pDC->FillRect(&CClientRect,&m_CBkBrush);
    return TRUE;
}

Als nächstes fügen Sie der Klasse noch die Membervariable m_CBkBrush   für den Pinsel zum Ausfüllen des Fensterhintergrundes hinzu. Erstellen Sie in der OnInitialUpdate(...) Methode den Pinsel wie folgt:

void CGDIFuncView::OnInitialUpdate()
{
    ....
    for (int iLoop=1; iLoop<m_nNoOfPts; iLoop++)
    {
        ....
    }
    // Pinsel fuer Fensterhintergrund erstellen
    m_CBkBrush.CreateSolidBrush(RGB(240,240,240));
}

Übersetzen und starten Sie das Programm nun wieder. Der Hintergrund des Fensters sollte jetzt in einem hellem grau erscheinen.

Damit beenden wir die Übersicht über die Flächenfunktionen und wenden uns in der nächsten Lektion der Beschreibung der verschiedenen Mapping-Modes zu. Mit Hilfe der Mapping-Modes werden wir dann unsere Messkurve fensterfüllend darstellen können.



Copyright © 2004

Senden Sie Emails mit Fragen oder Kommentaren zu dieser Website an: mailto:info@cpp-tutor.de
 Wolfgang Schröder, Lerchenweg 23, D-72805 Lichtenstein. Tel: +49 7129 6470