[Re-post; the first one didn't seem to go through]
Post by R.WieserActually, I'm going quite gaga here ... :-(
1) Load an image from file in such a way that I can draw on it (using
standard API-functions)
2) Draw on it (have not even reached this stage yet :-\ )
3) Save the (altered) image to a file
I'm quite sure that I can load & save an image, but only for specific
circumstances. Maybe my problem is that I would like to have my routines
generic, so I can save *any* image (mostly loaded into a DC) to file.
Just to name something I'm bumping against : I've loaded an image from a
file as normal, and as a DIB-section. For some reason I can get a palette
from the DC if it's contents are loaded as a DIB-section, but not when
loaded as a bitmap. Why ? A bitmap (<= 8 bpc) needs a palette too,
otherwise it's colors go AWOL ...
A DIB/DIBSection is device independent and stores it's custom palette as part of it's header structure, a DDB is device dependant
and is assumed to use the system palette. If it has it's own palette then a separate GDI palette object must be created, filled and
selected into the same DC as the DDB for it to be used - LoadImage() does not do this for you so if you want to load a paletted
image and keep the palette intact then use the LR_CREATEDIBSECTION flag.
Post by R.WieserAnd something I do not understand about DIB-sections : If a DIB contains a
BITMAPINFOHEADER (including a palette if relevant), why does a DIB-Section
have this information too ? Why does a DIB-Section have a BITMAPHEADER
when all of it's info is allso stored into the BITMAPINFOHEADER just below
the BITMAPHEADER ? In the light of this, what justifies it's existense ?
It just does not make sense ...
A DIB is really a rarely used creature since it is nothing more than a chunk of memory containing a header followed by the bitmap
data, often when people (including myself at times) write DIB they actually mean DIBSection.
A DIBSection is an encapsulation of a DIB into a GDI object which can be used with the rest of the GDI infrastructure allowing
drawing to be performed in a device independent manor. The BITMAPINFO structure is always present with a DIB or DIBSection but
since it has a dynamic array for the palette, non-paletted images (excluding for the moment bit-fields compressed images) are not
required to store this and so often to save space developers just reference the BITMAPINFOHEADER structure directly rather than
through the BITMAPINFO (the system doesn't care, it's only a pointer to some memory at the end of the day.)
Post by R.WieserYour help sure does, but I currently do not seem to be able to understand
the fine points of GDI-programming, and it is frustrating. Even something
simple as loading-and-saving an image goes beyond me :-\
Try this:
'***
Private Declare Function LoadImage Lib "User32.dll" Alias "LoadImageA" ( _
ByVal hInst As Long, ByVal lpsz As String, ByVal un1 As Long, _
ByVal n1 As Long, ByVal n2 As Long, ByVal un2 As Long) As Long
Private Declare Function GetObject Lib "GDI32.dll" Alias "GetObjectA" ( _
ByVal hObject As Long, ByVal nCount As Long, ByRef lpObject As Any) As Long
Private Declare Function DeleteObject Lib "GDI32.dll" ( _
ByVal hObject As Long) As Long
Private Declare Function GetDIBits Lib "GDI32.dll" (ByVal aHDC As Long, _
ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, _
ByRef lpBits As Any, ByRef lpBI As BitmapInfo8, ByVal wUsage As Long) As Long
Private Declare Function GetDC Lib "User32.dll" (ByVal hWnd As Long) As Long
Private Declare Function ReleaseDC Lib "User32.dll" ( _
ByVal hWnd As Long, ByVal hDC As Long) As Long
Private Declare Function CreateCompatibleDC Lib "GDI32.dll" ( _
ByVal hDC As Long) As Long
Private Declare Function SelectObject Lib "GDI32.dll" ( _
ByVal hDC As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteDC Lib "GDI32.dll" ( _
ByVal hDC As Long) As Long
Private Type Bitmap ' 24 bytes
bmType As Long
bmWidth As Long
bmHeight As Long
bmWidthBytes As Long
bmPlanes As Integer
bmBitsPixel As Integer
bmBits As Long
End Type
Private Type BitmapFileHeader ' 14 bytes
bfType As Integer
bfSize As Long
bfReserved1 As Integer
bfReserved2 As Integer
bfOffBits As Long
End Type
Private Type BitmapInfoHeader ' 40 bytes
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
Private Type BitmapInfo8 ' 1064 bytes
bmiHeader As BitmapInfoHeader
bmiColors(255) As Long
End Type
Private Const IMAGE_BITMAP As Long = &H0
Private Const LR_LOADFROMFILE As Long = &H10
Private Const LR_CREATEDIBSECTION As Long = &H2000
Private Const BI_BITFIELDS As Long = &H3
Private Sub Form_Load()
Dim hBMP As Long, hOldBMP As Long
Dim hDC As Long
SET FILE PATH'S HERE THEN REMOVE THIS LINE
Const ImgPath As String = "X:\Path\SrcFile.bmp"
Const SavePath As String = "X:\Path\DestFile.bmp"
' Attempt to load this file from disk
hBMP = LoadImage(App.hInstance, ImgPath, IMAGE_BITMAP, 0, 0, _
LR_LOADFROMFILE Or LR_CREATEDIBSECTION)
If (hBMP) Then ' Loaded ok, create a DC stick the Bitmap into it
hDC = CreateCompatibleDC(0)
hOldBMP = SelectObject(hDC, hBMP)
' Perform any drawing to hDC now
Call SelectObject(hDC, hOldBMP)
Call DeleteDC(hDC)
' Attempt to save Bitmap back out to disk
If (Not SaveDIB(hBMP, SavePath)) Then
Debug.Print "Error saving """ & SavePath & """"
End If
' Clean up
Call DeleteObject(hBMP)
Else ' Something went wrong during loading
Debug.Print "Error loading """ & ImgPath & """"
End If
End Sub
Private Function SaveDIB(ByVal inDIB As Long, ByRef inPath As String) As Boolean
Dim BMinf As Bitmap
Dim DIBHead As BitmapInfo8
Dim NumCols As Long
Dim DeskDC As Long
Dim BMData() As Long, DataLen As Long
Dim FileHead As BitmapFileHeader
Dim FNum As Integer
Dim WritePal As Long
Dim FileSize As Long
If (GetObject(inDIB, Len(BMinf), BMinf) = 0) Then Exit Function
If (BMinf.bmBits = 0) Then Exit Function ' Only working with DIBSection's
' Get a reference DC to work with
DeskDC = GetDC(0)
' Set Bitmap info header size
DIBHead.bmiHeader.biSize = Len(DIBHead.bmiHeader)
' Attempt to read DIBSection header
If (GetDIBits(DeskDC, inDIB, 0, 0, ByVal 0&, DIBHead, 0)) Then
If (DIBHead.bmiHeader.biBitCount <= 8) Then
NumCols = DIBHead.bmiHeader.biClrUsed ' Read palette
If (NumCols = 0) Then NumCols = 2 ^ DIBHead.bmiHeader.biBitCount
Call GetDIBits(DeskDC, inDIB, 0, 0, ByVal 0&, DIBHead, 0)
ElseIf (DIBHead.bmiHeader.biCompression) Then
If (DIBHead.bmiHeader.biCompression = BI_BITFIELDS) Then
NumCols = 3
Else ' Don't support RLE compressed images
Call ReleaseDC(0, DeskDC)
Exit Function
End If
End If
' Create image data buffer
DataLen = DIBHead.bmiHeader.biSizeImage \ 4
ReDim BMData(DataLen - 1) As Long
' Read image data
Call GetDIBits(DeskDC, inDIB, 0, _
DIBHead.bmiHeader.biHeight, BMData(0), DIBHead, 0)
DIBHead.bmiHeader.biClrUsed = NumCols
With FileHead
.bfType = &H4D42 ' Bitmap magic cookie; ASCII "BM"
.bfOffBits = Len(FileHead) + Len(DIBHead.bmiHeader) + (NumCols * 4)
.bfSize = DIBHead.bmiHeader.biSizeImage + .bfOffBits
End With
On Error Resume Next ' Check to see if the file already exists
FileSize = CBool(FileLen(inPath) + 1)
On Error GoTo 0
' If so, kill it
If (FileSize) Then Call Kill(inPath)
FNum = FreeFile() ' Get a free file handle
Open inPath For Binary Access Write Lock Read Write As #FNum
Put #FNum, , FileHead ' Write file header
Put #FNum, , DIBHead.bmiHeader ' Write Bitmap info header
If (NumCols) Then ' Write palette
For WritePal = 0 To NumCols - 1
Put #FNum, , DIBHead.bmiColors(WritePal)
Next WritePal
End If
Put #FNum, , BMData() ' Write image data
Close #FNum
' Return true
SaveDIB = True
End If
Call ReleaseDC(0, DeskDC)
End Function
'***
I've tested this with 1, 4, 8, 15 (Bit-fields), 16, 24 and 32-bit images and it seems to work ok here.
Hope this helps,
Mike
- Microsoft Visual Basic MVP -
E-Mail: ***@mvps.org
WWW: Http://www.mvps.org/EDais/