Note: Tests of successful loading of the image without applying the workaround have been done on various O/S. This patch appears to be required on Win7 and lower. Win8 not yet tested
You probably know that GDI+ is useful for loading various image formats, but GDI+ also has issues with every format it loads. For BMPs specifically, GDI+ has 1 major flaw in my opinion and another minor one:
a) Translucent bitmaps. 32 bit, 4 bytes per pixel, bitmaps can contain transparency just like other modern image formats. However, when GDI+ loads these, it reports back that the image format does not use the alpha channel even when the image actually does use it. This issue presents itself whether the RGB components are premultiplied against the alpha channel or not.
b) PNG/JPG embedded bitmaps. Huh? If you aren't aware these are possible, then that's probably why GDI+ doesn't directly support them. Besides, they weren't intended for display anyway, they were intended for printers. Anyway, a JPG or PNG file can be placed in a bitmap as the bitmap's pixel data. The only real change to the bitmap file format is that the bitmap's BitCount property must be zero, its SizeImage property must be the size of the JPG/PNG embedded file and that its Compression property be BI_PNG or BI_JPG as appropriate.
The attached txt file is a VB class. After you save it to disk, rename it to .cls
I've also included 4 bitmaps to play with:
1) One that uses semi-transparency
2) One that uses semi-transparency, but pixels are premultiplied against the alpha channel
3) One that has a JPG embedded into it
4) One that has a PNG embedded into it
So, to play.
1) Download the txt file & rename it .cls
2) Create a new project and add that class to your project
3) On the form, add a commandbutton
4) Paste this code to your form & run project
5) Drag/drop a BMP onto the form
See also:
GDI+ Workaround: JPG > Zero-Length APP Markers
GDI+ Workaround: TIFF > JPEG-compressed images
You probably know that GDI+ is useful for loading various image formats, but GDI+ also has issues with every format it loads. For BMPs specifically, GDI+ has 1 major flaw in my opinion and another minor one:
a) Translucent bitmaps. 32 bit, 4 bytes per pixel, bitmaps can contain transparency just like other modern image formats. However, when GDI+ loads these, it reports back that the image format does not use the alpha channel even when the image actually does use it. This issue presents itself whether the RGB components are premultiplied against the alpha channel or not.
b) PNG/JPG embedded bitmaps. Huh? If you aren't aware these are possible, then that's probably why GDI+ doesn't directly support them. Besides, they weren't intended for display anyway, they were intended for printers. Anyway, a JPG or PNG file can be placed in a bitmap as the bitmap's pixel data. The only real change to the bitmap file format is that the bitmap's BitCount property must be zero, its SizeImage property must be the size of the JPG/PNG embedded file and that its Compression property be BI_PNG or BI_JPG as appropriate.
The attached txt file is a VB class. After you save it to disk, rename it to .cls
I've also included 4 bitmaps to play with:
1) One that uses semi-transparency
2) One that uses semi-transparency, but pixels are premultiplied against the alpha channel
3) One that has a JPG embedded into it
4) One that has a PNG embedded into it
So, to play.
1) Download the txt file & rename it .cls
2) Create a new project and add that class to your project
3) On the form, add a commandbutton
4) Paste this code to your form & run project
5) Drag/drop a BMP onto the form
Code:
Option Explicit
Private Declare Function GdipDeleteGraphics Lib "GdiPlus.dll" (ByVal mGraphics As Long) As Long
Private Declare Function GdipDrawImageRectRectI Lib "GdiPlus.dll" (ByVal hGraphics As Long, ByVal hImage As Long, ByVal dstX As Long, ByVal dstY As Long, ByVal dstWidth As Long, ByVal dstHeight As Long, ByVal srcX As Long, ByVal srcY As Long, ByVal srcWidth As Long, ByVal srcHeight As Long, ByVal srcUnit As Long, ByVal imageAttributes As Long, ByVal Callback As Long, ByVal callbackData As Long) As Long
Private Declare Function GdipCreateFromHDC Lib "GdiPlus.dll" (ByVal hDC As Long, hGraphics As Long) As Long
Private Declare Sub GdiplusShutdown Lib "gdiplus" (ByVal Token As Long)
Private Declare Function GdiplusStartup Lib "gdiplus" (Token As Long, inputbuf As Any, Optional ByVal outputbuf As Long = 0) As Long
Private Type GdiplusStartupInput
GdiplusVersion As Long
DebugEventCallback As Long
SuppressBackgroundThread As Long
SuppressExternalCodecs As Long
End Type
Private m_Token As Long
Private m_BMP As cBMPreader
Private Sub Form_Load()
Call pvCreateToken
If m_Token = 0 Or m_Token = -1 Then
Me.Show
DoEvents
MsgBox "Failed to start up GDI+", vbExclamation + vbOKOnly
Unload Me
Exit Sub
End If
Set m_BMP = New cBMPreader
Me.Move (Screen.Width - 10245) \ 2, (Screen.Height - 6585) \ 2, 10245, 6585
Me.ScaleMode = vbPixels
Me.Command1.Move 0, 0
Me.Command1.Caption = "Refresh"
Me.OLEDropMode = vbOLEDropManual
End Sub
Private Sub Form_Unload(Cancel As Integer)
Set m_BMP = Nothing
If Not (m_Token = 0& Or m_Token = -1&) Then pvDestroyToken
End Sub
Private Function pvCreateToken() As Boolean
Dim GSI As GdiplusStartupInput
On Error Resume Next
If Not m_Token = -1 Then
GSI.GdiplusVersion = 1&
Call GdiplusStartup(m_Token, GSI)
If m_Token = 0 Then
m_Token = -1&
Else
pvCreateToken = True
End If
End If
End Function
Private Sub pvDestroyToken()
If Not (m_Token = 0 Or m_Token = -1) Then GdiplusShutdown m_Token
m_Token = 0&
End Sub
Private Sub Command1_Click()
Call pvRenderImage
End Sub
Private Sub pvRenderImage()
If Not m_BMP.Handle = 0& Then
Dim hGraphics As Long, w As Long, h As Long, sngRatio As Single
Dim X As Long, Y As Long, cx As Long, cy As Long
Const UnitPixel As Long = 2&
w = m_BMP.Width
h = m_BMP.Height
cy = Me.ScaleHeight - Command1.Height
If Me.ScaleWidth / w > cy / h Then
sngRatio = cy / h
Else
sngRatio = Me.ScaleWidth / w
End If
If sngRatio > 1! Then sngRatio = 1&
cx = w * sngRatio
cy = h * sngRatio
X = (Me.ScaleWidth - cx) \ 2
Y = ((Me.ScaleHeight - Command1.Height) - cy) \ 2 + Command1.Height
Me.Cls
GdipCreateFromHDC Me.hDC, hGraphics
GdipDrawImageRectRectI hGraphics, m_BMP.Handle, X, Y, cx, cy, 0, 0, w, h, UnitPixel, 0, 0, 0
GdipDeleteGraphics hGraphics
End If
End Sub
Private Sub Form_OLEDragDrop(Data As DataObject, Effect As Long, Button As Integer, Shift As Integer, X As Single, Y As Single)
If Data.Files.Count Then
m_BMP.FileName = Data.Files.Item(1)
Call pvRenderImage
End If
End Sub
GDI+ Workaround: JPG > Zero-Length APP Markers
GDI+ Workaround: TIFF > JPEG-compressed images