[Tutorial] GDI+

    • VB.NET

    Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von rylox.

      [Tutorial] GDI+

      So, hiermit veröffentliche ich mein "großes GDI+ Tutorial für VB.NET" und hoffe, dass der ein oder andere so einen leichten Einstieg in dieses Gebiet bekommt. Im Forum tauchen ja immer häufiger Fragen und Kritik zu gemachten Schritten in diesem Themenbereich auf. Ich bemühe mich so allgemein zu bleiben, dass das Verständnis erhalten bleibt aber trotzdem mehr als nur die Oberfläche angekratzt wird^^ Viel Spaß beim Lesen und Lernen, wer Fehler findet oder weiß wie etwas besser geht bitte melden - auch ich kann nicht alles wissen.

      1: Was ist GDI+?
      GDI+ ist die Abkürzung für "Graphics Device Interface" und stellt System von Windows zum Arbeiten und Zeichnen von Grafiken dar. Es übernimmt Aufgaben wie das Zeichnen von Linien, Darstellen von Texten und Grafiken, etc. In VB.NET greift man auf das "Graphics-Objekt" zurück um zeichnen zu können.


      2: Woher bekommt man das Graphics-Object?
      Um das Graphics-Object zu bekommen gibt es mehrere Möglichkeiten die je nach Situation zum Einsatz kommen:

      Das Graphics-Object im Paint-Event
      Wenn man auf Controls Zeichnen möchte (z.b. einen Panel oder Form1) sollte man das im Paint-Event tun. Dinge die mit GDI gezeichnet wurden "verschwinden" wenn z.b. ein anderes Fenster über die Zeichnung geschoben wird. Würde man das zeichnen irgendwo (siehe createGraphics) durchführen wären Grafikfehler die Folge. Im Paint-Event hingegen wird ein zerstörter Bereich (ein Bereich der für ungültig erklärt wurde) automatisch neu gezeichnet, richtig gesagt: das Paint-Event wird immer aufgerufen wenn die grafische Oberfläche bzw ein Teil von ihr für ungültig erklärt wurde. Um zu verdeutlichen wie Leistungsstark GDI+ ist: Die komplette Benutzeroberfläche von VB.NET Programmen wird mittels GDI+ gezeichnet - so lässt sich nicht nur auf die Controls zeichnen sonder man kann die Controls selbst zeichnen. Dies geschieht (meist) im Paint-Event und ist eine andere Baustelle^^
      So wird auf das Graphics-Object zugegriffen:

      VB.NET-Quellcode

      1. Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
      2. With e.Graphics
      3. '.DrawLine(...)
      4. '.FillRectangle(...)
      5. End With
      6. End Sub


      Das Grapics-Object einer Bitmap
      Eine weitere häufig genutze Möglichkeit ist das erstellen eines Graphics-Objektes einer Bitmap. Dies tut man wenn man z.B. einen Text (Name des Fotografen) auf eine Bilddatei schreiben will. Vorteil ist, dass man das Ergebnis abspeichern (JPG, BMP, PNG, etc) kann. Im Paint-Event ist das Bild hingegen nur zur Programmlaufzeit da. Bevor einige schlaue Leute auf die Idee kommen einfach alles auf eine Bitmap zu zeichnen und diese dann als Hintergrundbild einer Form zu setzten um das Paint-Event zu umgehen, sei gesagt, dass dies nicht Sinn der Sache ist^^ Das Zeichnen auf eine Grafik kann überall geschehen - es wird also kein Paint-Event etc gebraucht!

      VB.NET-Quellcode

      1. Dim tmpBitmap As New Bitmap("C:\bild.jpg")
      2. With Graphics.FromImage(tmpBitmap)
      3. '.DrawLine(...)
      4. '.DrawString(...)
      5. End With


      Weitere Möglichkeiten
      Es gibt auch noch andere Möglichkeiten an ein Graphics-Objekt zu kommen. Hier nenne ich mal das mehr oder weniger verhasste createGraphics (z.B. form1.createGraphics). Dieses ist eher unbeliebt, da viele Anfänger den Fehler machen dieses zu nutzen anstatt das Paint-Event. Der Nachteil ist, dass mit createGraphics nur genau einmal gezeichnet wird. Wird der Bereich ungültig verschwindet das Gezeichnete teilweise oder komplett. Ebenfalls gibt es Graphics.FromHwnd oder Graphics.FromHdc. Wie diese genau funktionieren weiß ich nicht genau und ich bin der Ansicht, dass dies auch zu Tief in die Materie geht.


      3: Einfache Zeichenfunktionen
      Ich denke jeder fängt mit einer einfachen Linie an und arbeitet sich dann nach und nach hoch. Da die Überladungen der ZeichnenFunktionen eigentlich selbsterklärend sind will ich nur ein paar Beispiele aufzeigen und erst im nächsten Unterpunkt erklären womit (sprich mit Pinsel oder Stift) gezeichnet wird.

      Linien zeichnen ist ganz einfach: Man braucht zwei Punkte und einen Stift (bzw man benutzt die von VB vorgegebenen). Einfach den folgenden Code ins Paint-Event und schon ist die Linie da:

      VB.NET-Quellcode

      1. Dim Punkt1 As New Point(50, 50)
      2. Dim Punkt2 As New Point(300, 100)
      3. With e.Graphics
      4. .DrawLine(Pens.Black, Punkt1, Punkt2) 'Benutzt den Standart-Stift Pens.Black und zeichnet eine Linie zwischen Punkt1 und Punkt2
      5. End With


      In diesem Beispiel wird eine Ellipse ausgefüllt (das geht nicht mit einem Stift sonder mit einem Pinsel => Brush), darüber werden die Umrisse der Ellipse gezeichnet (Linien werden mit einem Stift => Pen gezogen).

      VB.NET-Quellcode

      1. With e.Graphics
      2. .FillEllipse(Brushes.Yellow, 50, 50, 100, 100)
      3. .DrawEllipse(Pens.Black, 50, 50, 100, 100)
      4. End With




      Ebenfalls lässt sich Text recht einfach Zeichnen:

      VB.NET-Quellcode

      1. With e.Graphics
      2. .DrawString("MeinText", Font, New SolidBrush(ForeColor), 10, 10)
      3. End With


      Man kann schnell weitere Funktionen zum Zeichnen einfacher Formen (Rectangle für Rechtecke, Elipse für Kreise oder Elipsen, etc) finden. IntelliSense hilft dabei ungemein und zeigt auch gleich welche Daten benötigt werden (BSP Rechteck: Position X und Y sowie Breite und Höhe). Man wird feststellen, dass die Zeichenfunktionen welche mit "Draw" beginnen (DrawLine, DrawEllipse) immer eine Figur "umranden" und einen Pen benutzen. Hingegen benutzen die "Fill"-Funktionen einen Brush und füllen eine Figur aus.



      4: Unsere Zeichenwerkzeuge: Pen und Brush

      Brush:



      Der Brush (Pinsel) wird genutz um Grafikbereiche oder Formen auszufüllen. Im einfachsten Falle mit einer Farbe. Dazu deklarieren wir unseren Pinsel so:

      SolidBrush

      VB.NET-Quellcode

      1. Dim tmpBrush As New SolidBrush(Color.Red)

      mit diesem könnten wir nun irgendetwas Rot ausmalen (der Code gehört ins Paint-Event!):

      VB.NET-Quellcode

      1. With e.Graphics
      2. .FillRectangle(tmpBrush, 20, 20, 100, 100)
      3. End With

      Das Ganze ist recht unspektakulär, wir bauen allerdings darauf auf. Es gibt nämlich mehr als nur den langweiligen einfarben-Pinsel:

      TextureBrush
      Schon etwas cooler ist es eine Grafik (jpg-datei, etc) als Textur zu benutzen. An dieser Stelle sei gesagt, dass das laden der Bitmap aus einer Datei AUßERHALB der Paint-Routine geschehen sollte, da dies unperformant ist und nur einmal geschehen sollte - und dann eben nicht in einer recht Zeitkritischen Routine^^ Mit dem TextureBrush kann genau wie mit dem SolidBrush gearbeitet werden, so deklariert man ihn:

      VB.NET-Quellcode

      1. Dim tmpBrush As New TextureBrush(New Bitmap("C:\a.jpg"))


      HatchBrush
      Dieser Brush ist ähnlich dem TextureBrush, nur lässt er ausschließlich vordefinierte Muster zu - lediglich die Farben können frei gewählt werden. Dieser eignet sich besonders um z.B. Schrafuren darzustellen. Wie immer ist auch dieser zu benutzen wie der ganz normale SolidBrush:

      VB.NET-Quellcode

      1. Dim tmpBrush As New Drawing2D.HatchBrush(Drawing2D.HatchStyle.BackwardDiagonal, Color.Black, Color.White)




      LinearGradientBrush
      Um einen Farbverlauf darzustellen greift man am besten auf diesen Pinsel zu. Er fordert zwei Punkte und zwei Farben. Füllt man mit ihm eine Fläche aus herrscht bei Punkt1 die Farbe1. Diese verändert sich je näher man Punkt2 kommt immer mehr bis sie schließlich der Farbe2 entspricht. Dieses Muster wiederholt sich! Am besten ausprobieren denn dabei lernt man am besten! Zur deklaration:

      VB.NET-Quellcode

      1. Dim tmpBrush As New Drawing2D.LinearGradientBrush(New Point(0, 0), New Point(50, 50), Color.Red, Color.Black)


      PathGradientBrush
      Mit diesem Brush lassen sich auch komplexere Farbübergänge realisieren. Ich denke mal, dass dies ebenso ein Thema für sich werden kann und den Rahmen dieses Tutorials sprengt



      Pen:

      Kommen wir nun zum Pen. Dieser ist wie ein richtiger Stift - er zeichnet also Linien mit einer bestimmten Dicke. Die Dicke des Pens kann immer verändert werden - ebenso die Farbe mit der er zeichnet. Auch der Pen kann mehr als nur langweilige einfarbige Linien fabrizieren: Er verfügt über eine Eigenschaft "Brush" und damit stehen ihm zum zeichnen der Linien die Möglichkeiten der Pinsel offen. Diese sind dann allerdings auf die Dicke der Linie begrenzt - die Figur wird nicht ausgefüllt sondern nur die Umrandung.

      So deklariert man einen Schwarzen Stift mit einer Linienstärke von 4. Ohne Transformation bedeutet das, dass der Stift 4 Pixel dicke Linien zeichnet.

      VB.NET-Quellcode

      1. Dim tmpPen As New Pen(Color.Black, 4)


      Sieht man sich die Überladungen des Subs New an sieht man, dass er auch einen Brush- und einen Widh (LinienStärke)-Wert akzeptiert. Es ist also möglich z.B. Linien mit einer Textur zu zeichnen:

      VB.NET-Quellcode

      1. Dim tmpPen As New Pen(Color.Black, 4) 'Überladungen anschauen, spart evtl code!
      2. tmpPen.Width = 30 'ändert die Dicke des Stiftes fest
      3. tmpPen.Brush = New TextureBrush(New Bitmap("C:\a.jpg"))
      4. With e.Graphics
      5. .DrawEllipse(tmpPen, 50, 50, 100, 100)
      6. End With




      Der Pen kann noch mehr, so lassen sich beispielsweise die Linienenden oder Anfänge manipulieren - Pfeile darzustellen ist so ein Kinderspiel! Weitere Informationen über das Zeichnen mit Pens erhält man in einem anderen Tutorial von mir: Tutorial Pen & Linien in GDI+


      5: Transformation und Manipulation
      Oft sieht man, dass ganze Codeblöcke von Berechnungen geschrieben werden um eine Figur rotiert oder verschoben darzustellen. Jeder der sich das zumutet weiß offensichtlich nicht, dass das Graphics-Objekt ihm diese Mühen abnehmen kann und da es die Berechnungen sowieso durchführt vermutlich auch noch schneller ist! Ebenso verunstaltet man den eigenen Code beim nutzen den Transform-Eigenschaft nicht und hält ihn übersichtlich.

      VB.NET-Quellcode

      1. Dim TransMatrix As New Drawing2D.Matrix
      2. With TransMatrix
      3. .Shear(0.5, 0.4)
      4. .Rotate(63)
      5. .Translate(50, 300)
      6. .Scale(2, 2)
      7. ' .RotateAt(45, New Point(100, 100))'eine Mischung aus Verschieben und Rotieren
      8. End With
      9. e.Graphics.Transform = TransMatrix





      Häufig will man auch nur in einem bestimmten bereich Zeichnen, die anderen sollen, egal was man tut, unberührt bleiben. Mit SetClip kann man einen Bereich/Region festlegen in dem gezeichnet werden darf. Alles was über diesen hinausgeht wird ignoriert.

      VB.NET-Quellcode

      1. e.Graphics.SetClip(New Rectangle(50, 50, 100, 100))




      6: Einstellungen zur Qualitäts- oder Geschwindigkeitsverbesserung
      Das Graphics-Object beinhaltet viele Eigenschaften um die Renderqualität zu beeinflussen. Ich liste hier mal die besagten Eigenschaften auf. Eine Ausreichende Erklärung liefert IntelliSense. Ich sage an dieser stelle nur, dass man mit diesen Werten spielen muss! Oft sieht "hightQuality" nicht am besten aus - vor allem bei der Textdarstellung.

      VB.NET-Quellcode

      1. With e.Graphics
      2. .SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
      3. .TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAlias
      4. .PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality
      5. .InterpolationMode = Drawing2D.InterpolationMode.HighQualityBilinear
      6. .CompositingQuality = Drawing2D.CompositingQuality.HighQuality
      7. End With




      7: Erweiterte Möglichkeiten/Nützliches
      Auch das bisher gezeigte ist nur die Spitze des Eisberges! Jeder Brush beinhaltet eine Vielzahl von weiteren Eigenschaften und Funktionen. Das stöbern mittels Objektbrowser oder IntelliSense lohnt sich immer. Trotzdem werde ich nun ein paar weitere nützliche Funtionen von GDI+ zeigen.

      GraphicsPath
      Viele Effekte lassen sich mit dem GraphicsPath realisieren. Der GraphicsPath fasst dabei mehre Figuren oder auch Buchstaben zusammen. Das Einheitliche ausfüllen z.B. mit einem Farbverlauf oder Textur sind dann möglich. Ebenso lassen sich die Umrisse von Text nachzeichnen, was sonst nur sehr schwer möglich ist.

      VB.NET-Quellcode

      1. Dim GraPath As New Drawing2D.GraphicsPath
      2. GraPath.AddString("TEXT", Font.FontFamily, Font.Style, 150, New Point(20, 20), New StringFormat)
      3. Dim TexBrush As New TextureBrush(New Bitmap("C:\a.jpg"))
      4. Dim tmpPen As New Pen(Color.Black, 3)
      5. With e.Graphics
      6. .FillPath(TexBrush, GraPath)
      7. .DrawPath(tmpPen, GraPath)
      8. End With



      Was der GraphicsPath noch so kann

      MeasureString
      Das GraphicsObjekt bietet eine Funktion um zu "messen" welche Maße ein gerenderter Text später einnehmen würde. Nützlich ist dies z.B. wenn man eine Funktion schreiben möchte die die Schriftgröße automatisch an einen Rahmen etc anpasst.

      VB.NET-Quellcode

      1. With e.Graphics
      2. Dim PlatzBedarf As SizeF = .MeasureString("Text", Font)'Weitere Überladungen möglich!
      3. End With


      Bitmap-Funktionen nicht vergessen!
      Eine Funktion die ich häufiger benutze ist die .makeTransparent Funktion der Bitmap.

      Colors können mehr
      So verfügen die Colors neben dem RGB-Kanälen auch über den Alpha-Kanal. Dieser regelt die Transparenz der Farbe. Mittels Color.FromARGB(A,R,G,B) kann eine beliebige Farbe erstellt werden. Statt A = 255 einfach mal A = 100 setzten und schon hat man eine Transparente Farbe. Ideal um Bereiche zu Heighleighten!



      Links:
      Für Controlbauer: Beispielprojekt Listbox erweitern
      GDI Tutorial: Tutorial Pen & Linien in GDI+
      Für Fortgeschrittene: Control mit beweglicher Figur und Gezieltes OwnerDrawing

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „FreakJNS“ ()

      Tiefgehenderes Programmieren mit GrahpicsPath im Zusammenhang mit der Tranformations-Matrix

      Hier ein kleines Update: Nach einer hilfreichen PN vom ErfinderDesRades habe ich mich entschlossen das angebrochene Thema hier kurz zu erläutern. Man will z.B. eine Landkarte zeichnen - viele (ähnlich aussehende) Objekte sind auf dieser zu finden (Häuser, Bäume, etc). Ähnlich bedeutet in diesem Zusammenhang nur, dass sie Verschoben/Verzerrt/Rotiert sind. Geht man einen Schritt weiter wird man feststellen, dass man für gleiche Objekte den gleichen Graphicspath nutzen kann (das Rad muss nicht immer wieder neu erfunden werden :thumbsup:), man muss ihn nur mittels einer Transform-Matrix an die jeweilige Situation anpassen. Das Resultat ist ein Performancevorteil, aber nur ein kleiner. Einen richtig Großen Vorteil kann man sich erarbeiten wenn es um das Neuzeichnen geht. Häufig verändert sich ja nur ein kleiner Ausschnitt des ganzen (z.B. ein Baum wird entfernt oder die Position eines Hauses wird angepasst). Statt alles neu zu zeichnen kannn man dort exakt (ja exakt - man hat ja den GraphicsPath der den genauen Umriss angibt) die Bereiche neuzeichnen lassen (siehe Überladung von .Invalidate) die sich verändert haben: Alte Position (da wo das Haus vorher war) und Neue Position (wo das Haus jetzt steht). Ein solches Vorgehen, wenn es Teil eines durchdachten Konzeptes ist, kann enorme Performance-Vorteile bringen.
      Diese Herangehensweise ist wie man schon merkt etwas komplexer und schießt, wie der ErfinderDesRades mir schon schrieb, über das Niveau dieses Tutorials hinaus. Wer sich weiter in dieses Themengebiet einarbeiten will sollte mit diesen beiden Links gut bedient sein: Control mit beweglicher Figur und Gezieltes OwnerDrawing

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „FreakJNS“ ()

      Farbverläufe mit dem LinearGradientBrush

      (Ich hänge hier mal meine Forschungs-Ergebnisse zum LinearGradientBrush an - Danke für die Erlaubnis an @FreakJNS:)

      Der LinearGradientBrush hat im wesentlichen 2 Konstruktoren, von denen der eine leicht zu verstehen ist, aber unpraktisch, und der andere ist schwer zu verstehen, aber in der Anwendung isser praktisch :D

      Der leichte Konstruktor
      Einfache Sache: Eine Linie definieren (zB 45° geneigt), entlang derer die Farbe von Rot nach Schwarz verläuft:

      VB.NET-Quellcode

      1. Dim brsh = New LinearGradientBrush(New Point(0, 0), New Point(200, 200), Color.Red, Color.Black)


      Sieht in einem Quadrat der Kantenlänge 200 ganz gut aus, nämlich so (der gelbe Pfeil - Zusatz markiert die Verlaufs-Linie):


      Aber nun hat man ja ein flexibles Layout, und wenn man das Quadrat größer zieht, dann erscheinen (hässliche) Wiederholungen des FarbVerlaufes:


      Und in die Breite gezogen:


      Also das kann man so nicht abliefern :thumbdown:
      Man will ja wohl einen Farbverlauf im Rechteck, und zwar von ObenLinks (Ecke#1) nach UntenRechts (Ecke#4). Und keine Wiederholungen. Wenn man also ein schickes Label präsentieren will, mit einem Farbverlauf als Hintergrund, und durch Form-Resizing ändert dieses seine Größe und Proportion, dann muß man v.a. wieder good old Resizing-Code schreiben, weil LinearGradientBrush ist zu doof, sich automatisch der Control-Size anzupassen :(.

      Und dann wirds auf einmal richtig kompliziert, denn wie rechnet man den Endpunkt des LinearGradientBrush' aus, zu einem beliebig proportionierten Rechteck?
      Ich kanns nicht ausrechnen, aber die Umkehrung kann ich wenigstens aufzeigen: Etwa das Rechteck mit Kantenlänge 300|100 passt genau zum obigen LinearGradientBrush, sodass die EndColor genau auf Ecke#4 fällt:


      Ist das Rechteck breiter, so erscheint die hässliche Wiederholung - Ist es schmaler, so wird der Farbverlauf nicht bis zur Endfarbe geführt:
      . . .

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

      Der praktische Konstruktor

      Ja, und hier kommt nun der praktische Konstruktor ins Spiel, der rechnet sowas nämlich für uns aus. Also was sind die Bedingungen im o.g. Beispiel?
      1. Der Farbverlauf soll um 45° geneigt sein
      2. die End-Farbe soll auf Ecke#4 fallen

      VB.NET-Quellcode

      1. Dim brsh = New LinearGradientBrush(rct, .Color.Red, Color.Black, 45)
      Der füllt uns nun brav das rct aus, mit dem Verlaufswinkel 45°, und die Endfarbe wird auf Ecke#4 gelegt.
      Wir wissen nun zwar nicht mehr die Länge des Verlaufes, aber dafür passtes bei jeder Größe und Proportion:
      . . . :D

      Aber das ist immer noch nicht wirklich immer dolle. Etwa bei überproportional breiten Rechtecken sieht der Farbverlauf gar nicht mehr geneigt aus (also von Ecke zu Ecke), sondern ist von einem schlicht horizontalem Verlauf kaum noch zu unterscheiden:
      8|

      Hübscher wäre doch, wenn auch der Winkel sich an die Höhe/Breite-Proportion anpassen würde, oder? Und dafür hat der praktische Konstruktor noch eine Überladung, die dieses angibt:

      VB.NET-Quellcode

      1. Dim brsh = New LinearGradientBrush(rct, .Color.Red, Color.Black, 45, isAngleScaleable:=True)
      Nun weiß man nichtmal mehr den wirklichen Winkel (ausser bei Quadraten), aber sieht gut aus:
      :D
      Bei 45° Verlaufswinkel fällt die 50%-FarbMischung übrigens immer genau auf die Ecken #2 + #3 des Rechtecks.

      Blenden
      Mit der Blend-Klasse kann man "Wegmarken" im Farbverlauf definieren. Dabei geben die Factors die Mischung an der betreffenden Position an: 0.0F steht für Start-Farbe, 1.0F steht für die End-Farbe - Werte dazwischen stehen für Farben dazwischen ;)
      Der folgende Blend etwa gibt an, mit der EndFarbe zu beginnen - hin zur StartFarbe in der Mitte, und dann wieder zur EndFarbe am Ende:

      VB.NET-Quellcode

      1. Dim _Blend As New Blend With {.Factors = {1.0F, 0.0F, 1.0F}, .Positions = {0.0F, 0.5F, 1.0F}}
      2. brsh.Blend = _Blend


      InterpolationColors
      LinearGradientBrush kann auch noch mehr Farben, und das geht auch so mit "Wegmarken" - Dieser etwa macht inne Mitte grün:

      VB.NET-Quellcode

      1. Dim _ColorBlend As New ColorBlend() With {.Colors = {Color.Red, Color.Green, Color.Blue}, .Positions = {0.0F, 0.5F, 1.0F}}
      2. brsh.InterpolationColors = _ColorBlend


      Disposen
      wie gesagt, wir haben nun wieder das Vergnügen, Resizing-Code schreiben zu müssen, und im Resizing immer den Brush auszutauschen. Nun ist Brush Disposable, d.h. wenn der alte Brush also nicht mehr gebraucht wird, muss seine .Dispose-Methode aufgerufen werden, sonst sind da ganz schnell die Resourcen hunderter von Brushes belegt, und nicht mehr freigegeben. Einen Brush sauber austauschen:

      VB.NET-Quellcode

      1. ''' <summary> tauscht den LinearGradientBrush durch einen anneren aus, wobei die Colors übernommen werden </summary>
      2. <Extension()> _
      3. Public Sub Exchange(ByRef brsh As LinearGradientBrush, rct As Rectangle, angle As Single, Optional isAngleScalable As Boolean = False)
      4. With brsh
      5. Dim br = New LinearGradientBrush(rct, .LinearColors(0), .LinearColors(1), angle, isAngleScalable)
      6. If .Blend IsNot Nothing Then br.Blend = .Blend
      7. .Dispose()
      8. brsh = br
      9. End With
      10. End Sub


      letzte Links:
      Dateien

      Dieser Beitrag wurde bereits 10 mal editiert, zuletzt von „ErfinderDesRades“ ()

      Und wer beim Arbeiten mit LinearGradientBrushs einen komischen Bug antrifft, durch den die erste Reihe an Pixeln statt in der Startfarbe in der Endfarbe gezeichnet wird, dem sei dieser Post nahegelegt: stackoverflow.com/questions/11…brush-artifact-workaround

      Da habe ich auch schon ewig den Fehler in meinem Code gesucht, während es scheinbar ein Bug ist.

      Skybird schrieb:

      Das sind ja Ubisoftmethoden hier !