Surfaces laden van Custom Binary Resource bestanden
Inderdaad, dit is mogelijk! Wat hebben we nodig? Dit al zeker:
Declare Function StretchDIBits Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal dx As Long, ByVal dy As Long, ByVal SrcX As Long, ByVal SrcY As Long, ByVal wSrcWidth As Long, ByVal wSrcHeight As Long, lpBits As Any, lpBitsInfo As BITMAPINFO, ByVal wUsage As Long, ByVal dwRop As Long) As Long
Global Const SRCCOPY = &HCC0020
Global Const DIB_RGB_COLORS = 0
Inderdaad dit zijn API (Application Program Interface) calls. We roepen dus de hulp in van het Windows-framewerk. Je kan "SRCCOPY" ook veranderen door de 'vbSrcCopy' waarde die al intern is in Visual Basic.
Als je de functie wat ontleedt zie je al weldra dat het volgende onderdelen gebruikt:
1. Een hDC (Handle Device Context)
2. X, Y variabelen
3. Een pointer van het type 'Long' naar je bitmap data en één naar een BITMAPINFO structure
4. Een paar flags
De functie gebruikt de data van de BITMAPINFO structure en 'stretcht' (aanpassen van de grootte) het volgens de bron en doel coördinaten die je doorgeeft, en vervolgens blit de functie de afbeelding naar de DC die je eveneens hebt bepaald.
Dit kan je dus gebruiken met PictureBoxes, eze controls hebben namelijk elk een hDC, en met DirectDraw Surfaces. Met DDraw moet je de "DirectDrawSurface7.GetDC" methode gebruiken om de DC te verkrijgen, en "DirectDrawSurface7.ReleaseDC" nadien om de DC vrij te geven. Het 'releasen' (terug vrijgeven) van de DC is zéér belangrijk. Als je dit niet doet barst je computer van de 'Automation Errors' die dan opspringen!
Hoe krijg je de data en de BITMAPINFO? Héhé... Hier is't!
'Bitmap file format structures
Type BITMAPFILEHEADER
bfType As Integer
bfSize As Long
bfReserved1 As Integer
bfReserved2 As Integer
bfOffBits As Long
End Type
Type BITMAPINFOHEADER
biSize As Long
biWidth As Long
biHeight As Long
biPlanes As Integer
biBitCount As Integer
biCompression As Long
biSizeImage As Long
biXPelsPerMeter As Long
biYPelsPerMeter As Long
biClrUsed As Long
biClrImportant As Long
End Type
Type RGBQUAD
rgbBlue As Byte
rgbGreen As Byte
rgbRed As Byte
rgbReserved As Byte
End Type
Type BITMAPINFO
bmiHeader As BITMAPINFOHEADER
bmiColors(0 To 255) As RGBQUAD
End Type
Global gudtBMPFileHeader As BITMAPFILEHEADER 'Holds the file header
Global gudtBMPInfo As BITMAPINFO 'Holds the bitmap info
Global gudtBMPData() As Byte 'Holds the pixel data
Sub ExtractData(strFileName As String, lngOffset As Long)
Dim intBMPFile As Integer
Dim i As Integer
'Init variables
Erase gudtBMPInfo.bmiColors
'Open the bitmap
intBMPFile = FreeFile()
Open strFileName For Binary Access Read Lock Write As intBMPFile
'Fill the File Header structure
Get intBMPFile, lngOffset, gudtBMPFileHeader
'Fill the Info structure
Get intBMPFile, , gudtBMPInfo.bmiHeader
If gudtBMPInfo.bmiHeader.biClrUsed <> 0 Then
For i = 0 To gudtBMPInfo.bmiHeader.biClrUsed - 1
Get intBMPFile, , gudtBMPInfo.bmiColors(i).rgbBlue
Get intBMPFile, , gudtBMPInfo.bmiColors(i).rgbGreen
Get intBMPFile, , gudtBMPInfo.bmiColors(i).rgbRed
Get intBMPFile, , gudtBMPInfo.bmiColors(i).rgbReserved
Next i
ElseIf gudtBMPInfo.bmiHeader.biBitCount = 8 Then
Get intBMPFile, , gudtBMPInfo.bmiColors
End If
'Size the BMPData array
If gudtBMPInfo.bmiHeader.biBitCount = 8 Then
ReDim gudtBMPData(FileSize(gudtBMPInfo.bmiHeader.biWidth, gudtBMPInfo.bmiHeader.biHeight))
Else
ReDim gudtBMPData(gudtBMPInfo.bmiHeader.biSizeImage - 1)
End If
'Fill the BMPData array
Get intBMPFile, , gudtBMPData
'Ensure info is correct
If gudtBMPInfo.bmiHeader.biBitCount = 8 Then
gudtBMPFileHeader.bfOffBits = 1078
gudtBMPInfo.bmiHeader.biSizeImage = FileSize(gudtBMPInfo.bmiHeader.biWidth, gudtBMPInfo.bmiHeader.biHeight)
gudtBMPInfo.bmiHeader.biClrUsed = 0
gudtBMPInfo.bmiHeader.biClrImportant = 0
gudtBMPInfo.bmiHeader.biXPelsPerMeter = 0
gudtBMPInfo.bmiHeader.biYPelsPerMeter = 0
End If
Close intBMPFile
End Sub
Private Function FileSize(lngWidth As Long, lngHeight As Long) As Long
'Return the size of the image portion of the bitmap
If lngWidth Mod 4 > 0 Then
FileSize = ((lngWidth \ 4) + 1) * 4 * lngHeight - 1
Else
FileSize = lngWidth * lngHeight - 1
End If
End Function
Oké... euhm...
De structures die gedeclareerd worden zijn standaard componenten van eenderwelk bitmap bestand. Ze worden gebruikt in de 'ExtractData' subroutine om de bitmap van de binary file in elkaar te puzzelen.
ExtractData aanvaardt twee argumenten. De eerste (strFilename) is, zoals je al kan vermoeden, de naam van het bestand waarvan we gaan 'extracten' (bitmap eruit halen). De tweede (lngOffset) is de 'offset' binnen het bestand zelf waar de bitmap data begint. Dus het start punt van het bestand waar de bitmap begint. Deze functie kan gebruikt worden op 'custom binary resources' EN standaard bitmap bestanden. Bij standaard bitmaps gebruik je wel '1' als waarde voor "lngOffset".
Eens we deze twee gegevens hebben, kunnen we het bestand openen, vanaf de locatie gegeven door lngOffset, en de "gudtBMPFileHeader" informatie extracten. De volgende stap is "gudtBMPInfo.bmiHeader" extracten op dezelfde wijze, maar dan wordt voor de meesten onder jullie wat wazig...
We zouden graag de colour table data (kleurtabel gegevens) - indien dit een 8bit bitmap is - verkrijgen, maar niet alle programma's slagen de gegevens op op dezelfde wijze. Sommige slagen slechts een set entry-nummers op, als aangegeven door de
gudtBMPInfo.bmiHeader.biClrUsed variabele. Als deze waarde '0' is daarentegen, kunnen we een volledig kleuren-complement veronderstellen (256, indien 8bit) en gewoon extracten. Anders moeten we een loop doen door elke waarde en het manueel extracten.
Het volgende punt is de bitmap data (de bitmap gegevens). We moeten onze gudtBMPData byte array correct instellen, en dit kan lastig zijn met 8bit bitmaps. De reden hiervoor is dat ALLE bitmap scan lines (horizontale lijn pixels) moeten eindigen met een 32bit boundary, dus zijn ze soms opgevuld met blanke, lege bits. Om dit propbleem te omzeilen wordt er in de code gebruik gemaakt van de "FileSize" functie die de echte breedte en hoogte van de bitmap gaat berekenen. 24bit bitmaps veroorzaken dit probleem niet, en daardoor kunnen we de waarde van "gudtBMPInfo.bmiHeader.biSizeImage" als juist beschouwen.
Al wat ons nu nog te doen staat is om onze gbytBMPData array te verkrijgen en voilà! We hebben een bitmap in het geheugen! De overige code verbetert de fouten die gemaakt worden door 'brol'-programma's bij het wegschrijven van 8bit bitmaps met minder dan 256 kleuren. Zulke bitmaps zijn lichtjes trager om te laden, dus de code converteert ze in een sneller formaat.
En om af te sluiten:
StretchDIBits lngDC, 0, 0, gudtBMPInfo.bmiHeader.biWidth, gudtBMPInfo.bmiHeader.biHeight, 0, 0, gudtBMPInfo.bmiHeader.biWidth, gudtBMPInfo.bmiHeader.biHeight, gudtBMPData(0), gudtBMPInfo, DIB_RGB_COLORS, SRCCOPY
Verkrijg je DC (van een DirectDraw Surface of van een PictureBox), plaats het in
lngDC en de bovenstaande code zal de rest wel handelen. Het blit (zonder stretchen) de bitmap in het geheugen naar de DC die je hebt doorgegeven.
Dit is een mooie, subtiele vervanging voor DirectDraw7.CreateSurfaceFromFile.
Klik hier om een voorbeeld te downloaden, dat dit illustreert.
Translated by Djamel Grine