Bildgrafik mit zum Rand hin zunehmender Transparenz

  • VB.NET
  • .NET (FX) 4.0

Es gibt 15 Antworten in diesem Thema. Der letzte Beitrag () ist von Gather.

    Bildgrafik mit zum Rand hin zunehmender Transparenz

    Ich möchte eine elliptische Bitmap mit einer zum Rand hin zunehmenden Transparenz versehen, so dass die Bitmap stetig in eine beliebige Hintergrundgrafik übergeht. Viele Bildbearbeitungsprogramme können das. Ich möchte es in VB mit Hilfe von GDI+ Funktionen erreichen. Das angefügte Beispiel zeigt das Anliegen:



    Bis her habe ich u.A. mit dem Pathgradientbrush gespielt, aber noch keine Lösung gefunden. Damit lassen sich zwar Flächen mit Farbübergängen (auch Transparenzen) versehen, aber nicht in Verbindung mit Fotos. Vielleicht ist es auch der falsche Weg. Von der Sache her ist eine Bitmap des gewünschten Bildes mit Alpha-Werten zu versehen, die von 255 im Mittelteil zum Rand hin gegen 0 gehen.

    Leider habe ich im Netz zwar viele Hinweise auf Farbfüllungen mit Pinseln (brushes) gefunden, aber keinen der in Richtung meines Anliegens geht.

    Hat jemand eine Idee ???
    @drschef Es sieht so aus, als ob Du das ganze "zu Fuß" machen musst.
    Lade Dir die Datei in eine Bitmap-Instanz und übertrage diese pixelweise mit dem gewünschten Alpha-Wert in eine zweite Bitmap-Instanz, die Du dann anzeigst.
    Hier mal ein schneller Test:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. Dim bmp = My.Resources.Überziehen
    3. Dim bmp2 = New Bitmap(bmp.Width, bmp.Height)
    4. Dim a = 0
    5. For y = 0 To 511
    6. For x = 0 To bmp.Width - 1
    7. Dim col = bmp.GetPixel(x, y)
    8. bmp2.SetPixel(x, y, Color.FromArgb(a, col))
    9. Next
    10. If y Mod 2 = 0 Then
    11. a += 1
    12. End If
    13. If a >= 255 Then
    14. Exit For
    15. End If
    16. Next
    17. Me.BackgroundImage = bmp2
    18. End Sub
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hallo und danke für den Tip "pixelweise", sogar mit Beispiel. Das wollt ich aber möglichst vermeiden, denn es kostet Zeit. Ich hoffte und hoffe immer noch, dass es da einen Trick gibt, wo Du z.B. eine Bitmap auf eine andere drauf haust und damit fertig. Mißtrauisch macht nur, dass ich schon bei meinen Suchen nicht fündig geworden bin. Aber die Hoffnunng stirbt zuletzt.

    Inzwischen probier ich mal die pixelweise Änderung.
    @drschef Wenn Du mit LockBits arbeitest, sollte es auch sehr schnell gehen, allerdings auch pixelweise.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Habe gerade den letzten Hinweis gelesen. Danke!

    Mit der Pixelmethode habe ich bei einem 200-Pixel-Bild 100 ms gemessen. Ob ausreichend oder nicht, zeigt sich erst in der Menge.
    Lockbits wird sicher eine Steigerung bringen. Damit habe ich auch schon mal gearbeitet und werde die Anregung für alle Fälle im Hinterkopf behalten. Ich bin aber inzwischen auf einer anderen Spur, von der ich hoffe, dass sie Erfolg bringt. Das Schlagwort ist 'Opacity Mask'. Ist eine BItmap in Schwarz-Weiß bzw. Grautönen, bei der Schwarz für nicht transparent und weiß für voll transparent steht.

    Ich habe gerade begonnen, diesen Weg auszuprobieren. Wird aber heute nicht mehr fertig. Ich melde mich wahrscheinlich morgen.

    Schönen Abend.
    @drschef Das klingt sehr elegant.
    Erfreue uns mit Deiner Lösung. :thumbsup:
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hallo,

    ich habe ne Menge Zeit in die Recherche zum Thema 'Opacity mask' investiert. Microsoft gebraucht das Wort Durchlässigkeitsmaske, was schon mal gut klingt. Bin aber noch nicht zu einem Durchbruch gekommen. Für alle die es interressiert, nachfolgend schon mal eine Refernz für einen Einstieg.

    Inzwischen habe ich den 'Pixelweg' in meinen Rahmenalgorithmus weitgehend eingearbeitet, sowohl ohne Lockbits als auch mit. Für ein Ergebnis ist es noch zu früh. Ich werde mich morgen wieder melden.


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

    @drschef OK, WPF, da bin ich leider nicht zu Hause.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich habe eine Menge Internet-Beiträge zur Durchlässigkeitsmaske gefunden, wie zum Beispiel

    msdn.microsoft.com/de-de/library/ms743320(v=vs.110).aspx

    was nach WPF aussah, wovon ich leider auch keine Ahnung habe. Aber ich bin eigentlich davon ausgegangen, dass für diese Transparenzübergänge auch in VB was existieren müsste, gerade weil diese in der Zu-Fuß-Programmierung langsam und aufwendig sind. Tatsächlich habe ich auch einen echten C#-Beitrag gefunden, der mir aber mit den Klassen DrawingGroup und GeometryDrawing aus der System.Windows.Media auch Schwierigkeiten bereitet hat, so dass ich meine Forschung in dieser Richtung erst mal unterbreche. Im Folgenden der LInk dazu.

    msdn.microsoft.com/en-us/library/ms753195(v=vs.85).aspx

    Das Anliegen ist doch eigentlich ganz einfach: Bitmap 1 (Foto) und Bitmap 2 (Grautonbild) kombiniert ergeben eine modifizierte Bitmap 1 bei der der Alpha-Wert durch die Tönung von Bitmap 2 gesteuert wird.

    So habe ich mich auf die Pixelmethode in Verbindung mit LockBits konzentriert und eine auf den ersten Blick hinreichend schnelle Lösung erzielt. Ich habe damit gleichzeitig Geschwindigkeitstests mit und ohne Lockbits verbunden, die für Lockbits eine etwas 3-fach höhere Geschwindigkeit erbracht haben.
    Hier ist mal der Quellcode meines Testbeispiels:

    [vbnet] Private Sub PBX_Paint(sender As Object, e As System.Windows.Forms.PaintEventArgs) Handles PBX.Paint
    Me.Cursor = System.Windows.Forms.Cursors.WaitCursor

    Dim MitLockBits As Boolean = True
    'MitLockBits = False

    Dim zyk As Integer = 1 'Anzahl Wiederholungen zur genaueren Zeitmessung
    'zyk = 1 'Mit Lockbits 0,290 Sek
    'zyk = 25 'Mit Lockbits 0,567 Sek
    'zyk = 50 'Mit Lockbits 0,956 Sek

    'zyk = 1 'Ohne Lockbits 0,77 Sek
    'zyk = 25 'Ohne Lockbits 1,382 Sek
    'zyk = 50 'Ohne Lockbits 2,670 Sek

    Dim G As Graphics = e.Graphics
    Dim ds As Integer = 10
    Dim w As Integer = PBX.ClientSize.Width
    Dim h As Integer = PBX.ClientSize.Height
    Dim whalb As Integer = CInt(0.5 * w)
    Dim gr1dim As Single = CInt(0.36 * whalb) '1-dim. Grenze Beginn Tranparenz
    Dim gr2dim As Integer = CInt(Math.Abs(Math.Sqrt(gr1dim ^ 2 + gr1dim ^ 2))) '2-dim- Grenze
    Dim BT As Bitmap = Nothing
    Dim xx, yy As Integer 'Pixelkoordinaten
    Dim Alpha As Byte 'Wert der Überdeckung (Alpha)

    G.DrawImage(My.Resources.TestBack, 0, 0) 'Hintergrund zeichnen

    TimeStart()

    For i = 1 To zyk 'Mehrfache Wiederholung wegen besserer Zeitmessung
    BT = New Bitmap(My.Resources.Testbild) 'Beispielbild mit 200 x 200 Pixel
    BT.MakeTransparent(Color.White)

    If MitLockBits Then
    Dim rect As New Rectangle(0, 0, BT.Width, BT.Height)
    Dim bmpData As System.Drawing.Imaging.BitmapData = BT.LockBits(rect, _
    Drawing.Imaging.ImageLockMode.ReadWrite, BT.PixelFormat)
    Dim ptr As IntPtr = bmpData.Scan0

    'Array zur Speicherung aller Farb-Bytes der Bitmap
    Dim AnzBytes As Integer = Math.Abs(bmpData.Stride) * BT.Height
    Dim RGBValues(AnzBytes - 1) As Byte

    ' Copy the RGB values into the array.
    System.Runtime.InteropServices.Marshal.Copy(ptr, RGBValues, 0, AnzBytes)
    Dim argb As Integer = 4

    For y = 0 To w - 1
    For x = 0 To w - 1
    xx = x - whalb
    yy = y - whalb
    Dim r As Integer = CInt(Math.Abs(Math.Sqrt(xx ^ 2 + yy ^ 2)))

    'Änderung der Transparenz für ringförmigen Streifen
    If r - gr2dim >= 0 And r <= whalb - ds + 1 Then
    'Stärke der Überdeckung
    Alpha = CByte(Math.Abs(255 * (r - gr2dim) / (whalb - gr2dim - ds) - 255))

    'Transparenz setzen
    Dim position As Integer = y * BT.Width * argb + x * argb
    RGBValues(position + 3) = Alpha
    End If
    Next
    Next

    System.Runtime.InteropServices.Marshal.Copy(RGBValues, 0, ptr, RGBValues.Length)
    BT.UnlockBits(bmpData)
    Else
    For y = 0 To w - 1
    For x = 0 To w - 1
    xx = x - whalb
    yy = y - whalb
    Dim r As Integer = CInt(Math.Abs(Math.Sqrt(xx ^ 2 + yy ^ 2)))

    'Änderung der Transparenz für ringförmigen Streifen
    If r - gr2dim >= 0 And r <= whalb - ds + 1 Then
    Alpha = CByte(Math.Abs(255 * (r - gr2dim) / (whalb - gr2dim - ds) - 255))

    Dim col = BT.GetPixel(x, y)
    BT.SetPixel(x, y, Color.FromArgb(Alpha, col))
    End If
    Next
    Next
    End If
    Next

    G.DrawImage(BT, 0, 0)

    TimeStop()

    Me.Cursor = System.Windows.Forms.Cursors.Default
    End Sub

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

    @drschef Ich hab mal kurz drübergeschaut.
    Dim whalb As Integer = CInt(0.5 * w) machst Du Dim whalb As Integer = w \ 2 - Integer-Division
    Dim r As Integer = CInt(Math.Abs(Math.Sqrt(xx ^ 2 + yy ^ 2))) machst Du Dim r As Integer = CInt(Math.Sqrt(xx * xx + yy * yy)) - Das bringt echt Zeit in der inneren Schleife
    und das wichtigste:
    Nimm den Code aus der Paint-Routine raus.
    Wenn Du während der Rechnung das Fenster vergrößerst, kommt er mehrfach in die Paint-Routine und es knallt.
    Also:
    In einer Button-Click alles in eine Bitmap malen und die einfach der Image-Property zuweisen.
    Der Code ist praktisch identisch.
    Bitmap erstellen, von der ein Graphics-Objekt erstellen lassen, da reinmalen. Feddich
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Und wenn du noch mehr Performance haben möchtest, dann mach es in C# mit unsafe und pointern, da kannst dir das ganze hin und herkopieren sparen, außerdem arraygrenzen testen kostet auch Zeit und fällt bei Pointern auch weg.
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Danke für die Hinweise.

    Zunächst zu den Empfehlungen von Rod...

    Danke für den praktischen Hinweis. Die empfohlenen Rechenregeln habe ich sofort eingebaut. Der andere grundsätzliche Hinweis, die Grafikbefehle aus dem Paint-Event heraus zu lösen, ist in meinem Algorithmus leider nicht möglich. Es geht um eine Retuscherfunktion für Fotos. Da wird an einer Stelle ein Ausschnit aus dem Foto heraus kopiert und an einer zweiten Stelle wieder hinein kopiert. So wird mit dm Cursor mit jeder Verschiebung ein Paint-Ereignis ausgelöst und an anderer Stelle eine Kopie fortgeschrieben. Die Gefahr, dass da bei der Fenstergröße was schiefgeht, existiert tatsächlich nicht. Das Fenster in dem es passiert, ist während des Retuschiervorgangs fixiert. Sonst würde der ganze Vorgang scheitern.

    Nun zu jvbsl:
    Der Hinweis auf C# ist sicher richtig. Aber ich habe meine ersten Programme 1969 geschrieben und seitdem ein Dutzend Rehnergenerationen und Sprachen hinter mich gebracht. Ich glaube, da kann ich auf mildernde Umstände hoffen, wenn ich den späten Umstieg auf C# vermeide.

    Ich wollte mal mein Zwischenergebnis hier graphisch einbinden. Aber ich finde leider keine Möglichkeit hier einen grafischen Dateianhang hochzuladen und einzubinden.

    drschef schrieb:

    grafischen Dateianhang
    Erweiterte Antwort => Dateianhänge => Hochladen.
    Allerdings wundere ich mich, denn Du hast doch bereits Bilder hochgeladen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hallo und danke,

    ja ich hatte schon Bilder hochgeladen, das ging genau auf dem Weg mit Dateianhängen und 'In Text einfügen'. Entweder war ich da durch Zufall drauf gestoßen oder die Option Dateianhänge ist bei 'Neues Thema' sichtbar und dann bei Antworten nicht sofort. Jedenfalls danke!



    Das ist nun das Bild, welches der obige Algorithmus bei einmaliger Anwendung liefert. Aber schon tut sich das nächste Problem auf! Im Prinzip wird ein Ausschnitt von einer bestimmten Stelle entnommen und an einer anderen Stelle wieder drübergeschrieben. Das wird aber problematisch, wenn man beim Maus.Move-Event permanent neu zeichnet und die Kreischeiben weitgehend übereinander gezeichnet werden. Da addieren sich nämlich die teiltransparenten Anteile am Rand der Spur und ergeben immer weniger Transparenz. Dem wäre zu begegnen, indem man aus der geclippten Region wieder den Teil herausnimmt, der zu dem vorherigen Kopiervorgang gehört, so dass die gleichen Pixel nicht mehrfach beschrieben werden. Hier scheint sich ein weiteres Zeitproblem aufzutun. Jedenfalls bin ich noch am Probieren.
    Immer noch gibt es eine Reihe von Problemen, die aber zu weit ins Detail gehen und ich brauch erst wieder mal etwas Abstand. Aber hier schon mal ein Ergebnis:



    Die linke der beiden Personen ist ein echter Klon. Gibt es als nicht nur in der Medizin, erstellt mit der obigen Technologie. Wenn die Funktion etwas mehr ausgereift ist und ihren endgültigen Platz gefunden hat, melde ich mich nochmal.

    Danke nochmal an die Berater.
    Ich würde es wie du zu Beginn schon erwähnt hast mit einem Pathgradientbrush machen.
    In einem Projekt von mir habe ich prinzipiell genau dies gemacht: Controlsammlung: SteamControls - Ein modernes DarkUI für deine Anwendung! (unter dem Abschnitt Diverses)
    Mfg: Gather
    Private Nachrichten bezüglich VB-Fragen werden Ignoriert!