Die Community zu .NET und Classic VB.
Menü

VB.NET-Tipp 0059: Bitmapobjekt in ein 8bppIndexed-Graustufen-Bitmapformat konvertieren

 von 

Beschreibung

Durch einen Bug in der GDI+ v.5xx kann kein Graphics-Objekt von den Indexed-Bitmapformaten erstellt werden. Dadurch kann kein Bitmap-Objekt, mit einer anderen Farbtiefe, so einfach in eines der Indexed-Bitmapformate konvertiert werden. Dieses Beispiel zeigt wie ein Bitmapobjekt in ein 8bppIndexed-Graustufen-Bitmapobjekt konvertiert werden kann.

Schwierigkeitsgrad:

Schwierigkeitsgrad 2

Framework-Version(en):

.NET Framework 2.0, .NET Framework 3.0, .NET Framework 3.5

.NET-Version(en):

Visual Basic 2005, Visual Basic 2008

Download:

Download des Beispielprojektes [49,24 KB]

' Dieser Quellcode stammt von http://www.activevb.de
' und kann frei verwendet werden. Für eventuelle Schäden
' wird nicht gehaftet.

' Um Fehler oder Fragen zu klären, nutzen Sie bitte unser Forum.
' Ansonsten viel Spaß und Erfolg mit diesem Source!

' Projektversion:   Visual Studio 2005
' Option Strict:    An
'
' Referenzen: 
'  - System
'  - System.Data
'  - System.Deployment
'  - System.Drawing
'  - System.Windows.Forms
'  - System.Xml
'
' Imports: 
'  - Microsoft.VisualBasic
'  - System
'  - System.Collections
'  - System.Collections.Generic
'  - System.Data
'  - System.Drawing
'  - System.Diagnostics
'  - System.Windows.Forms
'

' ##############################################################################
' ################################# Form1.vb ###################################
' ##############################################################################
Option Strict On
Option Explicit On

Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices

Public Class Form1
    ' Speichert die fertige Graustufenpalette
    Private Bmp8GrayPal As ColorPalette

    ''' <summary>
    ''' Konvertiert ein Bitmapobjekt in ein 8bppIndexedGrayScale Bitmapobjekt
    ''' </summary>
    ''' <param name="InBitmap">Bitmapobjekt</param>
    ''' <returns>8bppIndexedGrayScale Bitmapobjekt</returns>
    Private Function ConvertTo8bppIndexedGrayScale( _
        ByVal InBitmap As Bitmap) As Bitmap

        ' Für die For/Next Schleifen
        Dim intX As New Integer
        Dim intY As New Integer

        ' Für die Pixelpositionen im ByteArray
        Dim Bmp8Pos As New Integer
        Dim Bmp24Pos As New Integer

        ' Breite einer Bildzeile inkl. PadBytes berechnen
        Dim Bmp8Stride As Integer = (InBitmap.Width + 3) And Not 3
        Dim Bmp24Stride As Integer = ((InBitmap.Width * 3) + 3) And Not 3

        ' ByteArrays zur Aufnahme der gesamten Bitmapdaten dimensionieren
        Dim Bmp8Data As Byte() = _
            New Byte((InBitmap.Height * Bmp8Stride) - 1) {}
        Dim Bmp24Data As Byte() = _
            New Byte((InBitmap.Height * Bmp24Stride) - 1) {}

        ' Handle auf das gepinnte ByteArray Bmp24Data holen
        Dim hBmp24Data As GCHandle = _
            GCHandle.Alloc(Bmp24Data, GCHandleType.Pinned)

        ' InBitmap in ein 24bppRGB Bitmap konvertieren -> Bmp24
        ' Gleichzeitig stehen uns im gepinntem ByteArray Bmp24Data die 
        ' Bitmapdaten von Bmp24 zur Verfügung
        Using Bmp24 As New Bitmap(InBitmap.Width, InBitmap.Height, _
            Bmp24Stride, PixelFormat.Format24bppRgb, _
            hBmp24Data.AddrOfPinnedObject())

            ' Graphicsobjekt von Bmp24 erstellen -> Bmp24Gra
            Using Bmp24Gra As Graphics = Graphics.FromImage(Bmp24)

                ' InBitmap in das Graphicsobjekt zeichnen
                Bmp24Gra.DrawImageUnscaledAndClipped(InBitmap, _
                    New Rectangle(0, 0, Bmp24.Width, Bmp24.Height))

                ' Bmp24Gra löschen (dispose)
            End Using

            ' alle Pixel durchlaufen
            For intY = 0 To Bmp24.Height - 1
                For intX = 0 To Bmp24.Width - 1

                    ' Pixelpositionen in den ByteArrays berechnen
                    Bmp24Pos = (intY * Bmp24Stride) + (intX * 3)
                    Bmp8Pos = (intY * Bmp8Stride) + intX

                    ' RGB-Farbe zu Index in der 8bpp-Palette umrechnen
                    ' der berechnete Index verweist auf eine Farbe in der
                    ' Farbpalette -> Bmp8GrayPal
                    Bmp8Data(Bmp8Pos) = _
                        CByte(((77 * Bmp24Data(Bmp24Pos + 2)) + _
                            (150 * Bmp24Data(Bmp24Pos + 1)) + _
                            (28 * Bmp24Data(Bmp24Pos + 0))) \ 255)
                Next
            Next

            ' Bmp24 löschen (dispose)
        End Using

        ' Handle auf das gepinnte ByteArray Bmp24Data freigeben
        hBmp24Data.Free()

        ' Handle auf das gepinnte ByteArray Bmp8Data holen
        Dim hBmp8Data As GCHandle = _
            GCHandle.Alloc(Bmp8Data, GCHandleType.Pinned)

        ' Neues BitmapData-Objekt erstellen
        Dim NewBmp8BD As New BitmapData
        NewBmp8BD.Width = InBitmap.Width
        NewBmp8BD.Height = InBitmap.Height
        NewBmp8BD.Stride = Bmp8Stride
        NewBmp8BD.PixelFormat = PixelFormat.Format8bppIndexed
        ' Zeiger auf das gepinnte ByteArray
        NewBmp8BD.Scan0 = hBmp8Data.AddrOfPinnedObject()

        ' Neues 8bppIndexed Bitmap erstellen -> Bmp8
        Dim Bmp8 As New Bitmap(InBitmap.Width, InBitmap.Height, _
            PixelFormat.Format8bppIndexed)

        ' Bitmapdaten von Bmp8 sperren und die Daten von NewBmp8BD übertragen
        Dim Bmp8BD As BitmapData = _
            Bmp8.LockBits(New Rectangle(0, 0, Bmp8.Width, Bmp8.Height), _
            ImageLockMode.WriteOnly Or ImageLockMode.UserInputBuffer, _
            Bmp8.PixelFormat, NewBmp8BD)

        ' Sperrung der Bitmapdaten aufheben
        Bmp8.UnlockBits(Bmp8BD)

        ' Handle auf das gepinnte ByteArray Bmp8Data freigeben
        hBmp8Data.Free()

        ' Standard-Farbpalette durch die Graustufenpalette ersetzen
        Bmp8.Palette = Bmp8GrayPal

        Return Bmp8
    End Function

    Private Sub Button1_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles Button1.Click

        ' ist ein Bild in der PictureBox vorhanden
        If Not PictureBox2.Image Is Nothing Then
            ' Bild löschen  
            PictureBox2.Image.Dispose()
        End If

        PictureBox2.Image = _
            ConvertTo8bppIndexedGrayScale(CType(PictureBox1.Image, Bitmap))
    End Sub

    Private Sub Form1_Load(ByVal sender As Object, _
        ByVal e As System.EventArgs) Handles Me.Load

        ' Für die For/Next Scleife
        Dim intItem As New Integer

        ' 1x1 8bppIndexed-Bitmap temporär zum auslesen der Farbpalette erstellen
        Using tmpBmp8 As New Bitmap(1, 1, PixelFormat.Format8bppIndexed)

            ' aktuelle Farbpalette von tmpBmp8 auslesen
            ' diese enthält zu diesem Zeitpunkt bereits
            ' eine Standard-Farbpalette die wir durch eine
            ' neue Graustufenfarbpalette ersetzen
            Bmp8GrayPal = tmpBmp8.Palette

            ' alle Paletteneinträge durchlaufen
            For intItem = 0 To Bmp8GrayPal.Entries.Length - 1

                ' aktuelle Farbe in der Palette durch eine neue
                ' Graustufenfarbe ersetzen
                Bmp8GrayPal.Entries(intItem) = _
                    Color.FromArgb(255, intItem, intItem, intItem)
            Next

            ' tmpBmp8 löschen
        End Using

        ' Die erzeugte Graustufenpalette kann mehrfach verwendet werden und muss
        ' so nicht jedesmal beim Konvertieren neu erstellt werden, was dem 
        ' Ganzen nochmal einen kleinen Geschwindigkeitsschub verleiht.

        ' Bild aus der Ressource laden
        PictureBox1.Image = My.Resources.City005
    End Sub
End Class

Ihre Meinung  

Falls Sie Fragen zu diesem Artikel haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.

Archivierte Nutzerkommentare 

Klicken Sie diesen Text an, wenn Sie die 1 archivierten Kommentare ansehen möchten.
Diese stammen noch von der Zeit, als es noch keine direkte Forenunterstützung für Fragen und Kommentare zu einzelnen Artikeln gab.
Aus Gründen der Vollständigkeit können Sie sich die ausgeblendeten Kommentare zu diesem Artikel aber gerne weiterhin ansehen.

Kommentar von Reto Tischhauser am 24.06.2011 um 11:19

Funktioniert perfekt ! vielen Dank !