home *** CD-ROM | disk | FTP | other *** search
/ Planet Source Code Jumbo …e CD Visual Basic 1 to 7 / 5_2007-2008.ISO / data / Zips / FYI__UserC204114152007.psc / c32bppDIB.cls < prev    next >
Text File  |  2007-01-04  |  38KB  |  913 lines

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4.   Persistable = 0  'NotPersistable
  5.   DataBindingBehavior = 0  'vbNone
  6.   DataSourceBehavior  = 0  'vbNone
  7.   MTSTransactionMode  = 0  'NotAnMTSObject
  8. END
  9. Attribute VB_Name = "c32bppDIB"
  10. Attribute VB_GlobalNameSpace = False
  11. Attribute VB_Creatable = True
  12. Attribute VB_PredeclaredId = False
  13. Attribute VB_Exposed = False
  14. Option Explicit
  15. ' Not truly Win95 compatible. Win95 compatiblity will take some work. Carles P.V.
  16. ' posted a very good stretch interpolation routine on PSC. That routine would be
  17. ' needed to stretch an alpha image with great quality, but then per pixel merging
  18. ' of the alpha image to the destination DC is also needed. If image is not alpha,
  19. ' then StretchBlt could be used for Win95, which is how this class will fall back to.
  20.  
  21. ' About 32bpp pre-multiplied RGB (pARGB) bitmaps, if you are not aware.
  22. '   - These are used specifically for the AlphaBlend API & are GDI+ compatible
  23. '   Advantages:
  24. '       - Images can be per-pixel alpha blended
  25. '       - Opacity can be simultaneously adjusted during rendering
  26. '       - AlphaBlend does both BitBlt & StretchBlt for pARGB images.
  27. '       - Speed: AlphaBlend & GDI+ are pretty quick APIs vs manual blending
  28. '   Disadvantages:
  29. '       - The original RGB values are permanently destroyed during pre-multiplying
  30. '           -- there is no way to convert pARGB back to non-premultiplied RGB values
  31. '           -- the formula would be: reconstructedRed=(preMultipliedRed * 255) \ Alpha.
  32. '               but because of integer division when pre-multiplying the result is only close
  33. '               and if this should be premultiplied again & converted again, the results can get worse
  34. '       - Displaying a pre-multiplied bitmap without AlphaBlend will not result in
  35. '           the image being displayed as expected.
  36. '       - Not ideal for saving due to its size: SizeOf= W x H x 4
  37. '           -- better to save source image instead or compress the DIB bytes using favorite compression utility
  38. '           -- with GDI+, image can be converted to PNG for storage
  39. '       - AlphaBlend & GDI+ are not supported on Win95
  40.  
  41. ' This class holds the 32bpp image. It also marshals any new image thru
  42. ' the battery of parsers to determine best method for converting the image
  43. ' to a 32bpp alpha-compatible image.
  44.  
  45. ' The parser order is very important for fastest/best results...
  46. ' cPNGparser :: will convert PNG, all bit depths; aborts quickly if not PNG
  47. ' cGIFparser :: will convert non-transparent/transparent GIFs; aborts quickly
  48. ' cICOpraser :: will convert XP-Alpha, paletted, true color, & Vista PNG icons
  49. '               -- can also convert most non-animated cursors
  50. ' cBMPparser :: will convert bitmaps, wmf/emf & jpgs
  51.  
  52. ' The parsers are efficient. Most image formats have a magic number that give
  53. '   a hint to what type of image the file/stream is. However, checks need to
  54. '   be employed because other files could feasibly have those same magic
  55. '   numbers. If the image is determined not to be one the parser is designed
  56. '   to handle, the parser rejects it and the next parser takes over.  The
  57. '   icon parser is slightly different because PNG files can be included into
  58. '   a Vista ico file. When this occurs, the icon parser will pass off the
  59. '   PNG format to the PNG parser automatically.
  60. ' And last but not least, the parsers have no advanced knowledge of the image
  61. ' format; as far as they are concerned anything passed is just a byte array
  62.  
  63. ' No APIs are declared public. This is to prevent possibly, differently
  64. ' declared APIs, or different versions of the same API, from conflciting
  65. ' with any APIs you declared in your project. Same rule for UDTs.
  66. ' Note: I did take some liberties in several API declarations throughout
  67.  
  68. ' CHANGE HISTORY
  69. ' 4 Jan 07:
  70. '   - Added LoadPicture_ByHandle, LoadPicture_StdPicture, ScaleImage & CopyImageTo
  71. '   - Added cPNGparser.SaveTo (testing). Requires GDI+ but will save 32bpp to PNG file or stream
  72. '       -- not accessible, right now, from c32bppDIB. Must initialize cPNGparser to use it.
  73. '   - Modified cICOparser's GetBestMatch algorithm
  74. '   - Added imgPNGicon as an image type to distinguish PNG in Vista Icon vs standard .PNG file
  75. '   - Bug found: removing overlays in cGIFparser.ConvertGIFto32bpp; forgot ByVal VarPtrArray(...)
  76. '       which could cause crash when compiled. Fixed & double checked everywhere else too
  77. ' 1 Jan 07:
  78. '   - Added SaveToFile & SaveToStream methods
  79. '   - cBMPparser could possibly try to query unauthorized memory; fixed
  80. '   - Methodology changed a bit when parsers return results. If image is definitely one
  81. '       that the parser is responsible for & the image is invalid, the parser will return
  82. '       True to prevent other parsers from handling the image. The c32bppDIB.Handle is used
  83. '       to determine true success or failure.
  84. '       -- cGIFparser when recognizing improperly formatted GIF would allow image to continue to
  85. '           other parsers which then may cause those parsers to lock up.
  86. ' 26 Dec 06: First version
  87.  
  88. ' Used to determine operating system
  89. Private Declare Function GetVersionEx Lib "kernel32" Alias "GetVersionExA" (lpVersionInformation As Any) As Long
  90. Private Const VER_PLATFORM_WIN32_WINDOWS As Long = &H1
  91. Private Type OSVERSIONINFOEX
  92.    dwOSVersionInfoSize As Long
  93.    dwMajorVersion As Long
  94.    dwMinorVersion As Long
  95.    dwBuildNumber As Long
  96.    dwPlatformId As Long
  97.    szCSDVersion As String * 128 ' up to here is OSVERSIONINFO vs EX
  98.    wServicePackMajor As Integer ' 14 bytes larger than OSVERSIONINFO
  99.    wServicePackMinor As Integer
  100.    wSuiteMask As Integer
  101.    wProductType As Byte
  102.    wReserved As Byte
  103. End Type
  104.  
  105. ' APIs used to manage the 32bpp DIB
  106. Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
  107. Private Declare Function CreateCompatibleDC Lib "gdi32.dll" (ByVal hDC As Long) As Long
  108. Private Declare Function GetDC Lib "user32.dll" (ByVal hwnd As Long) As Long
  109. Private Declare Function ReleaseDC Lib "user32.dll" (ByVal hwnd As Long, ByVal hDC As Long) As Long
  110. Private Declare Function DeleteDC Lib "gdi32.dll" (ByVal hDC As Long) As Long
  111. Private Declare Function SelectObject Lib "gdi32.dll" (ByVal hDC As Long, ByVal hObject As Long) As Long
  112. Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long
  113. Private Declare Function CreateDIBSection Lib "gdi32.dll" (ByVal hDC As Long, ByRef pBitmapInfo As Any, ByVal un As Long, ByRef pointer As Long, ByVal Handle As Long, ByVal dw As Long) As Long
  114. Private Declare Function AlphaBlend Lib "msimg32.dll" (ByVal hdcDest As Long, ByVal nXOriginDest As Long, ByVal nYOriginDest As Long, ByVal nWidthDest As Long, ByVal nHeightDest As Long, ByVal hdcSrc As Long, ByVal nXOriginSrc As Long, ByVal nYOriginSrc As Long, ByVal nWidthSrc As Long, ByVal nHeightSrc As Long, ByVal lBlendFunction As Long) As Long
  115. Private Declare Function StretchBlt Lib "gdi32.dll" (ByVal hDC As Long, ByVal x As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal nSrcWidth As Long, ByVal nSrcHeight As Long, ByVal dwRop As Long) As Long
  116. Private Declare Function SetStretchBltMode Lib "gdi32.dll" (ByVal hDC As Long, ByVal nStretchMode As Long) As Long
  117. Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" (lpPictDesc As PictDesc, riid As Any, ByVal fPictureOwnsHandle As Long, iPic As IPicture) As Long
  118. Private Declare Function GetObjectType Lib "gdi32.dll" (ByVal hgdiobj As Long) As Long
  119. Private Declare Function GetIconInfo Lib "user32.dll" (ByVal hIcon As Long, ByRef piconinfo As ICONINFO) As Long
  120. Private Const STRETCH_HALFTONE As Long = 4
  121. Private Const OBJ_BITMAP As Long = 7
  122. Private Const OBJ_METAFILE As Long = 9
  123. Private Const OBJ_ENHMETAFILE As Long = 13
  124.  
  125.  
  126. ' used when saving an image or part of the image
  127. Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (Ptr() As Any) As Long
  128. Private Type SafeArrayBound
  129.     cElements As Long
  130.     lLbound As Long
  131. End Type
  132. Private Type SafeArray
  133.     cDims As Integer
  134.     fFeatures As Integer
  135.     cbElements As Long
  136.     cLocks As Long
  137.     pvData As Long
  138.     rgSABound(0 To 1) As SafeArrayBound ' reusable UDT for 1 & 2 dim arrays
  139. End Type
  140.  
  141. Private Type PictDesc
  142.     Size As Long
  143.     Type As Long
  144.     hHandle As Long
  145.     hPal As Long
  146. End Type
  147. Private Type ICONINFO
  148.     fIcon As Long
  149.     xHotspot As Long
  150.     yHotspot As Long
  151.     hbmMask As Long
  152.     hbmColor As Long
  153. End Type
  154. Private Type BITMAPINFOHEADER
  155.     biSize As Long
  156.     biWidth As Long
  157.     biHeight As Long
  158.     biPlanes As Integer
  159.     biBitCount As Integer
  160.     biCompression As Long
  161.     biSizeImage As Long
  162.     biXPelsPerMeter As Long
  163.     biYPelsPerMeter As Long
  164.     biClrUsed As Long
  165.     biClrImportant As Long
  166. End Type
  167. Private Type BITMAPINFO
  168.     bmiHeader As BITMAPINFOHEADER
  169.     bmiPalette As Long
  170. End Type
  171.  
  172. Private Const AC_SRC_OVER = &H0
  173. Private Const AC_SRC_ALPHA = &H1
  174.  
  175. Public Enum eImageFormat    ' source image format
  176.     imgNone = 0    ' no image loaded
  177.     imgBitmap = 1  ' standard bitmap or jpg
  178.     imgIcon = 3    ' standard icon
  179.     imgWMF = 2     ' windows meta file
  180.     imgEMF = 4     ' enhanced WMF
  181.     imgCursor = 5  ' standard cursor
  182.     imgBmpARGB = 6  ' 32bpp bitmap where RGB is not pre-multiplied
  183.     imgBmpPARGB = 7 ' 32bpp bitmap where RGB is pre-multiplied
  184.     imgIconARGB = 8 ' XP-type icon; 32bpp ARGB
  185.     imgGIF = 9      ' gif; if class.Alpha=True, then transparent GIF
  186.     imgPNG = 10     ' PNG image
  187.     imgPNGicon = 11 ' PNG in icon file (Vista)
  188.     imgCursorARGB = 12 ' alpha blended cursors? do they exist yet?
  189. End Enum
  190.  
  191. Public Enum eScaleOptions
  192.     scaleToSize = 0         ' [Default] will always scale
  193.     scaleDownAsNeeded = 1   ' will only scale down if image won't fit
  194.     scaleStretch = 2        ' wll always stretch/distort
  195. End Enum
  196.  
  197. Private m_Handle As Long        ' handle to 32bpp DIB
  198. Private m_Pointer As Long       ' pointer to DIB bits
  199. Private m_Height As Long        ' height of DIB
  200. Private m_Width As Long         ' width of DIB
  201. Private m_hDC As Long           ' DC if self-managing one
  202. Private m_prevObj As Long       ' object deselected from DC when needed
  203. Private m_os9x As Long          ' 1=win9x, 3=win95
  204. Private m_Format As eImageFormat ' type of source image
  205. Private m_ManageDC As Boolean   ' does class manage its own DC
  206. Private m_AlphaImage As Boolean ' does the DIB contain alpha/transparency
  207.  
  208. Public Function LoadPicture_File(ByVal FileName As String, _
  209.                                 Optional ByVal iconCx As Long, _
  210.                                 Optional ByVal iconCy As Long) As Boolean
  211.  
  212.     ' PURPOSE: Convert passed image file into a 32bpp image
  213.     
  214.     ' Parameters.
  215.     ' FileName :: full path of file. Validation occurs before we continue
  216.     ' iconCx :: desired width of icon if file is an icon file. Default is 32x32
  217.     ' iconCy :: desired height of icon if file is an icon file. Default is 32x32
  218.  
  219.     On Error Resume Next
  220.     If Len(Dir$(FileName, vbArchive Or vbReadOnly Or vbSystem Or vbHidden)) = 0 Then Exit Function
  221.     If FileLen(FileName) < 57 Then Exit Function
  222.     ' no image file/stream can be less than 57 bytes and still be an image
  223.     If Err Then
  224.         Err.Clear
  225.         Exit Function
  226.     End If
  227.     Dim aDIB() As Byte  ' dummy array
  228.     LoadPicture_File = LoadPictureEx(FileName, aDIB(), iconCx, iconCy, 0, 0)
  229.     
  230. End Function
  231.  
  232. Public Function LoadPicture_Stream(inStream() As Byte, _
  233.                                     Optional ByVal iconCx As Long, _
  234.                                     Optional ByVal iconCy As Long, _
  235.                                     Optional ByVal streamStart As Long = 0, _
  236.                                     Optional ByVal streamLength As Long = 0) As Boolean
  237.     
  238.     ' PURPOSE: Convert passed array into a 32bpp image
  239.     
  240.     ' Parameters.
  241.     ' inStream:: byte stream containing the image. Validation occurs below
  242.     ' iconCx :: desired width of icon if file is an icon file. Default is 32x32
  243.     ' iconCy :: desired height of icon if file is an icon file. Default is 32x32
  244.     ' streamStart :: array position of 1st byte of the image file. Validated.
  245.     ' streamLength :: total length of the image file. Validated.
  246.     
  247.     If iparseIsArrayEmpty(Not inStream) Then Exit Function
  248.     If streamStart < LBound(inStream) Then streamStart = LBound(inStream)
  249.     If streamLength = 0 Then streamLength = UBound(inStream) - streamStart + 1
  250.     If streamLength < 57 Then Exit Function
  251.     ' no image file/stream can be less than 57 bytes and still be an image
  252.     LoadPicture_Stream = LoadPictureEx(vbNullString, inStream, iconCx, iconCy, streamStart, streamLength)
  253.  
  254. End Function
  255.  
  256. Public Function LoadPicture_Resource(ByVal ResIndex As Variant, ByVal ResSection As Variant, _
  257.                             Optional VbGlobal As IUnknown, _
  258.                             Optional ByVal iconCx As Long, _
  259.                             Optional ByVal iconCy As Long, _
  260.                             Optional ByVal streamStart As Long = 0, _
  261.                             Optional ByVal streamLength As Long = 0) As Boolean
  262.  
  263.     ' PURPOSE: Convert passed resource array into a 32bpp image
  264.     
  265.     ' Parameters.
  266.     ' ResIndex :: the resource file index (i.e., 101)
  267.     ' ResSection :: one of the selections or String value of your resource section
  268.     '       - i.e., vbResBitmap, vbResIcon, "Custom", etc
  269.     ' VbGlobal :: pass as VB.GLOBAL of the project containing the resource file
  270.     '       - Allows class to be mobile; can exist in DLL or OCX
  271.     '       - if not provided, class will use resource from existing workspace
  272.     '       - For example, if this class was in a compiled OCX, then the only way
  273.     '           to use the host's resource file is passing the host's VB.Global reference
  274.     ' iconCx :: desired width of icon if file is an icon file. Default is 32x32
  275.     ' iconCy :: desired height of icon if file is an icon file. Default is 32x32
  276.     ' streamStart :: array position of 1st byte of the image file. Validated.
  277.     ' streamLength :: total length of the image file. Validated.
  278.     '   -- See LoadPicture_Stream for the validation
  279.     
  280.     ' Tips:
  281.     ' 1) Store 32bpp bitmaps in the "Custom" resource always. Storing in the
  282.     '       Bitmap resource can change color depth depending on your screen settings
  283.     ' 2) Icons, normal bitmaps, & cursors are generally stored in their own sections
  284.     ' 3) All other types of images are normally stored in the "Custom" section
  285.     
  286.     On Error GoTo ExitRoutine
  287.     
  288.     DestroyDIB
  289.     
  290.     Dim oWorkSpace As VB.Global, tPic As StdPicture
  291.     
  292.     If VbGlobal Is Nothing Then
  293.         Set oWorkSpace = VB.Global
  294.     ElseIf TypeOf VbGlobal Is VB.Global Then
  295.         Set oWorkSpace = VbGlobal
  296.     Else
  297.         Set oWorkSpace = VB.Global
  298.     End If
  299.     
  300.     If VarType(ResSection) = vbString Then
  301.         Dim inStream() As Byte
  302.         ' could be anything, PNG,icon,gif,32bpp bitmap,wmf, etc
  303.         inStream = oWorkSpace.LoadResData(ResIndex, ResSection)
  304.         LoadPicture_Resource = LoadPicture_Stream(inStream, iconCx, iconCy, streamStart, streamLength)
  305.     Else
  306.         ' can only be single icon, bitmap or cursor
  307.         Set tPic = oWorkSpace.LoadResPicture(ResIndex, ResSection)
  308.         LoadPicture_StdPicture tPic
  309.     End If
  310.     LoadPicture_Resource = Not (m_Handle = 0)
  311.     
  312. ExitRoutine:
  313.     If Err Then Err.Clear
  314. End Function
  315.  
  316. Public Function LoadPicture_StdPicture(Picture As StdPicture) As Boolean
  317.  
  318.     ' PURPOSE: Convert passed stdPicture into a 32bpp image
  319.     
  320.     If Not Picture Is Nothing Then
  321.         If Picture.Type = vbPicTypeBitmap Then
  322.             Dim cBMP As New cBMPparser
  323.             Call cBMP.ConvertstdPicTo32bpp(Picture, Me, 0)
  324.             Set cBMP = Nothing
  325.         Else
  326.             Dim cICO As New cICOparser
  327.             Call cICO.ConvertstdPicTo32bpp(Picture, Me)
  328.             Set cICO = Nothing
  329.         End If
  330.         LoadPicture_StdPicture = Not (m_Handle = 0)
  331.     End If
  332.     
  333.  
  334. End Function
  335.  
  336. Public Function LoadPicture_ByHandle(Handle As Long) As Boolean
  337.  
  338.     ' PURPOSE: Convert passed image handle into a 32bpp image
  339.  
  340.     Dim icoInfo As ICONINFO, tPic As StdPicture
  341.     If Not Handle = 0 Then
  342.         Select Case GetObjectType(Handle)
  343.         Case OBJ_BITMAP, OBJ_METAFILE, OBJ_ENHMETAFILE
  344.             Set tPic = HandleToStdPicture(Handle, vbPicTypeBitmap)
  345.         Case Else
  346.             If Not GetIconInfo(Handle, icoInfo) = 0 Then
  347.                 If Not icoInfo.hbmColor = 0 Then DeleteObject icoInfo.hbmColor
  348.                 If Not icoInfo.hbmMask = 0 Then DeleteObject icoInfo.hbmMask
  349.                 Set tPic = HandleToStdPicture(Handle, vbPicTypeIcon)
  350.             End If
  351.         End Select
  352.         If Not tPic Is Nothing Then
  353.             LoadPicture_ByHandle = LoadPicture_StdPicture(tPic)
  354.         End If
  355.     End If
  356.     
  357. End Function
  358.  
  359. Public Sub ScaleImage(ByVal destWidth As Long, ByVal destHeight As Long, NewWidth As Long, NewHeight As Long, Optional ByVal ScaleMode As eScaleOptions = scaleDownAsNeeded)
  360.                             
  361.     ' Purpose: Returns the width and height needed to draw the image to the requested dimensions.
  362.     ' Function should be called before .Render should you want to scale the image.  Additionally,
  363.     ' scaling can assist in positioning image too, i.e., centering.
  364.     
  365.     ' destWidth [in]:: the width of the target canvas (drawing area)
  366.     ' destHeight [in]:: the height the target canvas
  367.     ' NewWidth [out]:: returns the width to use for the supplied ScaleMode
  368.     ' NewHeight [out]:: returns the height to use for the supplied ScaleMode
  369.     ' ScaleMode [in]::
  370.     '   scaleToSize [Default] - will always proportionally stretch the image to the target canvas size
  371.     '   scaleDownAsNeeded - will only shrink the image if needed; otherwise the original image size is passed
  372.     '   scaleStretch - the return value is always the canvas width and height; image distortion occurs
  373.                             
  374.     If m_Handle = 0& Then Exit Sub
  375.     
  376.     Dim RatioX As Single, RatioY As Single
  377.     ' calculate scale and offsets
  378.     Select Case ScaleMode
  379.     
  380.     Case scaleDownAsNeeded, scaleToSize: ' scaled
  381.         RatioX = destWidth / m_Width
  382.         RatioY = destHeight / m_Height
  383.         If ScaleMode = scaleDownAsNeeded Then
  384.             If RatioX > 1! And RatioY > 1! Then
  385.                 RatioX = 1!: RatioY = RatioX
  386.             End If
  387.         End If
  388.         If RatioX > RatioY Then RatioX = RatioY
  389.         NewWidth = Int(RatioX * m_Width)
  390.         NewHeight = Int(RatioX * m_Height)
  391.     
  392.         ' to center your image in the target canvas. Use the passed & returned parameters like so:
  393.         ' canvasX = (destWidth - NewWidth) \ 2 + any Left offset you may be using
  394.         ' canvasY = (destHeight - NewHeight) \ 2 + any Top offset you may be using
  395.         ' canvasX and canvasY would then be passed to .Render as .Render's X,Y parameters
  396.         
  397.     Case scaleStretch: ' stretch
  398.         NewWidth = m_Width
  399.         NewHeight = m_Height
  400.         
  401.     End Select
  402.  
  403.  
  404. End Sub
  405.  
  406. Public Sub CopyImageTo(cDIBclass As c32bppDIB)
  407.     
  408.     ' Function replicates the the current image to another DIB class
  409.     
  410.     If Not m_Handle = 0 Then                ' do we have an image to copy?
  411.         If Not cDIBclass Is Nothing Then    ' was a valid ref passed?
  412.             With cDIBclass
  413.                 .DestroyDIB                 ' kill ref's old DIB if any
  414.                 .InitializeDIB m_Width, m_Height    ' create new one & copy data
  415.                     If .BitsPointer = 0 Then Stop
  416.                 CopyMemory ByVal .BitsPointer, ByVal m_Pointer, m_Width * 4& * m_Height
  417.                 .Alpha = m_AlphaImage
  418.                 .ImageType = m_Format
  419.             End With
  420.         End If
  421.     End If
  422.     
  423. End Sub
  424.  
  425. Private Function LoadPictureEx(FileName As String, aStream() As Byte, _
  426.                             cX As Long, cy As Long, _
  427.                             streamOffset As Long, streamLength As Long) As Boolean
  428.     
  429.     ' PURPOSE: Marshal passed file/array to image classes for conversion to 32bpp image
  430.     ' For parameter information, see LoadPicture_File & LoadPicture_Stream
  431.     
  432.     Me.DestroyDIB
  433.     
  434.     If Not FileName = vbNullString Then ' file name was passed
  435.         
  436.         Dim FileNum As Integer
  437.         On Error Resume Next
  438.         
  439.         FileNum = FreeFile()    ' attempt to open file with read access only
  440.         Open FileName For Binary Access Read As #FileNum
  441.         ' if successful, we will use it later
  442.         If Err Then
  443.             Close #FileNum
  444.             Err.Clear
  445.             Exit Function
  446.         End If
  447.     End If
  448.     
  449.     ' various image parsers, in order of precedence
  450.     ' All 4 recognize transparency
  451.     Dim cPNG As cPNGparser  ' very fast to abort if not a PNG file
  452.     Dim cGIF As cGIFparser  ' very fast to abort if not a GIF file
  453.     Dim cICO As cICOparser  ' must parse key parts of a file. handles icons & Vista PNG Icons
  454.     Dim cBMP As cBMPparser  ' catchall. Handles bitmaps, wmf, emf & jpgs
  455.     
  456.     Dim bReturn As Boolean  ' function return value
  457.     
  458.     ' validate passed desired icon sizes
  459.     If cX < 0 Then cX = 0
  460.     If cy < 0 Then cy = 0
  461.     
  462.     Set cPNG = New cPNGparser   ' see if image is a PNG; aborts quickly if not
  463.     If FileName = vbNullString Then
  464.         bReturn = cPNG.LoadStream(aStream(), Me, streamOffset, streamLength)
  465.     Else     ' note: processing from file is slightly faster than via array
  466.         bReturn = cPNG.LoadFile(FileName, Me)
  467.         If bReturn = True Then Close #FileNum         ' close the file
  468.     End If
  469.     Set cPNG = Nothing
  470.     If Not bReturn Then
  471.         If Not FileName = vbNullString Then
  472.             streamOffset = 0&
  473.             streamLength = LOF(FileNum) ' cache length of file
  474.             ReDim aStream(streamOffset To streamLength - 1)
  475.             Get #FileNum, , aStream()   ' populate our stream with the file contents
  476.             Close #FileNum
  477.         End If
  478.         Set cGIF = New cGIFparser ' what about a GIF; aborts quickly if not
  479.         bReturn = cGIF.LoadStream(aStream(), Me, streamOffset, streamLength)
  480.         Set cGIF = Nothing
  481.         If Not bReturn Then
  482.             Set cICO = New cICOparser   ' will process Vista PNG icon if needed
  483.             bReturn = cICO.LoadStream(aStream(), cX, cy, Me, streamOffset, streamLength)
  484.             Set cICO = Nothing
  485.             If Not bReturn Then ' check for bmp, emf, wmf & jpg << last chance
  486.                 Set cBMP = New cBMPparser
  487.                 bReturn = cBMP.LoadStream(aStream(), Me, streamOffset, streamLength)
  488.                 Set cBMP = Nothing
  489.             End If
  490.         End If
  491.     End If
  492.     LoadPictureEx = Not (m_Handle = 0)
  493.  
  494. End Function
  495.  
  496. Friend Property Let Alpha(isAlpha As Boolean)
  497.     m_AlphaImage = isAlpha  ' determines the flags used for AlphaBlend API
  498.     ' this flag is set by the various image parsers; setting it yourself
  499.     ' can produce less than desirable effects.
  500.     ' Used in .Render, cPNGparser.SaveTo & .isWin95Alpha
  501. End Property
  502. Public Property Get Alpha() As Boolean
  503.     Alpha = m_AlphaImage
  504. End Property
  505.  
  506. Public Property Get ImageType() As eImageFormat
  507.     ImageType = m_Format    ' returns image format of the source image
  508. End Property
  509. Friend Property Let ImageType(iType As eImageFormat)
  510.     m_Format = iType    ' set by the various image parsers. This is not used
  511.     ' anywhere in these classes, you can do with it what you want -- for now.
  512. End Property
  513.  
  514. Public Property Get Width() As Long
  515.     Width = m_Width     ' width of image in pixels
  516. End Property
  517. Public Property Get Height() As Long
  518.     Height = m_Height   ' height of image in pixels
  519. End Property
  520. Public Property Get BitsPointer() As Long
  521.     BitsPointer = m_Pointer ' pointer to the bits of the image
  522. End Property
  523. Public Property Get ScanWidth() As Long
  524.     ScanWidth = m_Width * 4&    ' number of bytes per scan line
  525. End Property
  526. Public Property Get Handle() As Long
  527.     Handle = m_Handle   ' the picture handle of the image
  528. End Property
  529.  
  530. Public Function LoadDIBinDC(ByVal bLoad As Boolean) As Long
  531.  
  532.     ' Purpose: Select/Unselect the DIB into a DC.
  533.     ' Returns the DC handle when image is loaded
  534.     ' Called by image parser if it needs to paint the image into the DIB
  535.        
  536.     If bLoad = True Then
  537.         Dim tDC As Long
  538.         If Not m_Handle = 0 Then    ' do we have an image?
  539.             If m_hDC = 0 Then       ' do we have a DC?
  540.                 tDC = GetDC(0&)     ' if not create one
  541.                 m_hDC = CreateCompatibleDC(tDC)
  542.                 ReleaseDC 0&, tDC
  543.             End If
  544.             m_prevObj = SelectObject(m_hDC, m_Handle)
  545.             LoadDIBinDC = m_hDC
  546.         End If
  547.     Else
  548.         If Not m_prevObj = 0 Then
  549.             SelectObject m_hDC, m_prevObj
  550.             If m_ManageDC = False Then
  551.                 DeleteObject m_hDC
  552.                 m_hDC = 0
  553.             End If
  554.             m_prevObj = 0
  555.         End If
  556.     End If
  557. End Function
  558.  
  559. Public Property Let ManageOwnDC(bManage As Boolean)
  560.     ' Determines whether or not this class will manage its own DC
  561.     ' If false, then a DC is created each time the image needs to be Rendered
  562.     Dim tDC As Long
  563.     If bManage = False Then     ' removing management of DC
  564.         If Not m_hDC = 0 Then   ' DC does exist, destroy it
  565.             ' first remove the dib, if one exists
  566.             If Not m_Handle = 0 Then SelectObject m_hDC, m_prevObj
  567.             m_prevObj = 0
  568.         End If
  569.         DeleteDC m_hDC
  570.         m_hDC = 0
  571.     Else                        ' allowing creation of dc
  572.         If m_hDC = 0 Then       ' create DC only if we have a dib to put in it
  573.             If Not m_Handle = 0 Then
  574.                 tDC = GetDC(0&)
  575.                 m_hDC = CreateCompatibleDC(tDC)
  576.                 ReleaseDC 0&, tDC
  577.             End If
  578.         End If
  579.     End If
  580.     m_ManageDC = bManage
  581. End Property
  582. Public Property Get ManageOwnDC() As Boolean
  583.     ManageOwnDC = m_ManageDC
  584. End Property
  585.  
  586. Public Property Get isWin95Alpha() As Boolean
  587.     ' available if you wish to render alpha image manually.
  588.     isWin95Alpha = ((m_os9x = 3) And m_AlphaImage = True)
  589.     ' if property returns true & you will process the image for Win95, then...
  590.     ' 1) Call BitsPointer to retrieve the pre-multiplied bits pointer
  591.     ' 2) Process the image for Win95 using following algorithm
  592.     '   - If the DIBalphaByte (every 4th byte) = 0 then
  593.     '       skip the blending for that pixel
  594.     '   - Else (remember DIBs are BGR order, not RGB & DIB is bottom up
  595.     '       If the DIBalphaByte = 255 then
  596.     '           Set the destination pixel = DIB pixel, converting BGR to RGB as needed
  597.     '       Else
  598.     '       - Set lAlpha = 255&-DIBalphaByte to determine alpha of the destination pixel
  599.     '       - Then use following formula
  600.     '           desDCpixelRed = (lAlpha * desDCpixelRed) \ &HFF + DIBpixelRed
  601.     '           desDCpixelBlue = (lAlpha * desDCpixelBlue) \ &HFF + DIBpixelBlue
  602.     '           desDCpixelGreen = (lAlpha * desDCpixelGreen) \ &HFF + DIBpixelGreen
  603.     
  604. End Property
  605.  
  606.  
  607. Public Function InitializeDIB(ByVal Width As Long, ByVal Height As Long) As Boolean
  608.  
  609.     ' Creates a blank (all black, all transparent) DIB of requested height & width
  610.     
  611.     Dim tBMPI As BITMAPINFO, tDC As Long
  612.     
  613.     DestroyDIB ' clear any pre-existing dib
  614.     
  615.     If Width < 0 Then Exit Function
  616.     If Height < 0 Then
  617.         Height = Abs(Height) ' no top-down dibs
  618.     Else
  619.         If Height = 0 Then Exit Function
  620.     End If
  621.     
  622.     On Error Resume Next
  623.     With tBMPI.bmiHeader
  624.         .biBitCount = 32
  625.         .biHeight = Height
  626.         .biWidth = Width
  627.         .biPlanes = 1
  628.         .biSize = 40&
  629.         .biSizeImage = .biHeight * .biWidth * 4&
  630.     End With
  631.     If Err Then
  632.         Err.Clear
  633.         ' only possible error would be that Width*Height*4& is absolutely huge
  634.         Exit Function
  635.     End If
  636.     
  637.     If m_hDC = 0 Then
  638.         ' create a DC if class is managing its own & one isn't created yet
  639.         tDC = GetDC(0&) ' get screen DC regardless
  640.         If m_ManageDC = True Then m_hDC = CreateCompatibleDC(tDC)
  641.     Else
  642.         tDC = m_hDC ' use the class' DC, since we have it
  643.     End If
  644.     m_Handle = CreateDIBSection(tDC, tBMPI, 0, m_Pointer, 0, 0)
  645.     ' release the screen DC if we captured it
  646.     If Not tDC = m_hDC Then ReleaseDC 0&, tDC
  647.     
  648.     If Not m_Handle = 0 Then    ' let's hope system resources allowed DIB creation
  649.         m_Width = Width
  650.         m_Height = Height
  651.         InitializeDIB = True
  652.     End If
  653.  
  654. End Function
  655.  
  656. Public Sub DestroyDIB()
  657.     
  658.     ' PURPOSE: Destroy any existing image
  659.     If Not m_hDC = 0 Then   ' do we have a DC?
  660.         ' do we have an image; if so get it out of the DC
  661.         If Not m_prevObj = 0 Then SelectObject m_hDC, m_prevObj
  662.         ' destroy our DC, no point in keeping it w/o image
  663.         DeleteObject m_hDC
  664.         m_hDC = 0
  665.     End If
  666.     ' if we do have an image, destroy it now
  667.     If Not m_Handle = 0 Then DeleteObject m_Handle
  668.     ' reset other image attributes
  669.     m_Width = 0
  670.     m_Height = 0
  671.     m_Handle = 0
  672.     m_Pointer = 0
  673.     m_prevObj = 0
  674.     m_AlphaImage = False
  675.     m_Format = imgNone
  676. End Sub
  677.  
  678. Public Function Render(ByVal destinationDC As Long, _
  679.                 Optional ByVal x As Long, Optional ByVal Y As Long, _
  680.                 Optional dX As Long, Optional ByVal dY As Long, _
  681.                 Optional ByVal Opacity As Long = 100) As Boolean
  682.  
  683.     ' PURPOSE: Render an existing 32bpp DIB to a target DC
  684.     ' Note: Scaling, if needed, must be done before routine is called
  685.     ' Not Win95 compatible for alpha images. See isWin95Alpha for more.
  686.     
  687.     ' Parameters.
  688.     ' destinationDC :: target DC to draw to
  689.     ' X, Y :: the top/left coordinates to draw to, default is 0,0
  690.     ' dX, dY :: the width and height to draw to, default is the image's width & height
  691.     ' Opacity :: how opaque to draw the image, default is 100% opaque
  692.     '       -- not applicable if system is Win95
  693.  
  694.     Dim lBlendFunc As Long, tDC As Long, hOldImage As Long
  695.     Dim lStretchMode As Long
  696.     
  697.     ' validate a few things
  698.     If m_Handle = 0& Then
  699.         Exit Function
  700.     ElseIf destinationDC = 0& Then
  701.         Exit Function
  702.     End If
  703.     
  704.     If Opacity = 0 Then
  705.         Render = True
  706.         Exit Function   ' pointless if image is 100% transparent
  707.     Else
  708.         Opacity = Abs(Opacity) Mod 100
  709.         If Opacity = 0 Then Opacity = 100
  710.     End If
  711.     
  712.     If dX < 1 Then dX = m_Width
  713.     If dY < 1 Then dY = m_Height
  714.     
  715.     If m_hDC = 0& Then  ' do we have a DC to select our image into?
  716.         tDC = GetDC(0&) ' if not create one, if ManageOwnDC=True, we will have one
  717.         m_hDC = CreateCompatibleDC(tDC)
  718.         ReleaseDC 0&, tDC
  719.         hOldImage = SelectObject(m_hDC, m_Handle)
  720.     Else
  721.         ' we have a DC, but is the image selected into it?
  722.         If m_prevObj = 0 Then hOldImage = SelectObject(m_hDC, m_Handle)
  723.     End If
  724.     
  725.     If m_os9x = 3 Then ' win95, can't use AlphaBlend & Opacity is N/A
  726.         Render = Not (StretchBlt(destinationDC, x, Y, dX, dY, m_hDC, 0, 0, m_Width, m_Height, vbSrcCopy) = 0)
  727.         
  728.     Else
  729.         ' Stretch_Halftone not compatible with win9x
  730.         If m_os9x = 0 Then lStretchMode = SetStretchBltMode(destinationDC, STRETCH_HALFTONE)
  731.  
  732.         ' calcualte the opacity required & add it to the BlendFunction variable
  733.         lBlendFunc = AC_SRC_OVER Or ((CLng(255 * (Opacity / 100))) * &H10000)
  734.         ' if the image has transparency, then we add the AC_SRC_ALPHA flag too
  735.         If m_AlphaImage = True Then lBlendFunc = lBlendFunc Or (AC_SRC_ALPHA * &H1000000)
  736.         Render = Not (AlphaBlend(destinationDC, x, Y, dX, dY, m_hDC, 0, 0, m_Width, m_Height, lBlendFunc) = 0)
  737.         
  738.         If m_os9x = 0 Then SetStretchBltMode destinationDC, lStretchMode
  739.     
  740.     End If
  741.     
  742.     ' remove the image from the DC if necessary
  743.     If Not hOldImage = 0 Then SelectObject m_hDC, hOldImage
  744.     If Not tDC = 0& Then    ' if we created a DC, let's destroy it now
  745.         DeleteDC m_hDC
  746.         m_hDC = 0&
  747.     End If
  748.     
  749. End Function
  750.  
  751. Public Function SaveToFile(FileName As String, Optional PromptOverwrite As Boolean = True) As Boolean
  752.  
  753.     ' Should you want to save a 32bpp image to a file
  754.     ' Did you know? A 32bpp "XP-icon" saved in bitmap format is actually smaller
  755.     '               than saving it in an icon format....
  756.     '   BMP Format: 14byte header + 40byte BitmapInfo + 32bpp image bytes
  757.     '   ICO Format: 22byte header + 40byte BitmapInfo + 32bpp image bytes + 1bpp mask bytes
  758.     
  759.     ' FileName :: full path & name of file to be created
  760.     ' PromptOverwrite :: if True, the user will be offered an option to abort
  761.     '    if the target file already exists
  762.     
  763.     If FileName = vbNullString Then Exit Function
  764.     If m_Handle = 0 Then Exit Function
  765.     
  766.     On Error GoTo ExitRoutine
  767.     If Len(Dir(FileName, vbArchive Or vbHidden Or vbReadOnly Or vbSystem)) > 0 Then
  768.         If PromptOverwrite = True Then
  769.             If MsgBox("Overwrite current file?", vbYesNo + vbDefaultButton2 + vbQuestion, "Overwrite Confirmation") = vbNo Then
  770.                 Exit Function
  771.             End If
  772.         End If
  773.         SetAttr FileName, vbNormal ' in case the file was hidden, read only
  774.         Kill FileName
  775.     End If
  776.     
  777.     Dim FileNum As Integer, tBMPI As BITMAPINFO
  778.     Dim dibBits() As Byte
  779.     
  780.     With tBMPI.bmiHeader
  781.         .biHeight = m_Height
  782.         .biPlanes = 1
  783.         .biSize = 40
  784.         .biWidth = m_Width
  785.         .biBitCount = 32
  786.         .biSizeImage = .biWidth * .biHeight * 4&
  787.         ReDim dibBits(1 To .biSizeImage)
  788.         CopyMemory dibBits(1), ByVal m_Pointer, .biSizeImage
  789.     End With
  790.     
  791.     FileNum = FreeFile()
  792.     Open FileName For Binary As #FileNum
  793.     Put #FileNum, , CInt(&H4D42) ' bmp magic number
  794.     Put #FileNum, , CLng(54& + tBMPI.bmiHeader.biSizeImage) ' overall size of image
  795.     ' ^^ 54 = 14 byte bmp header + 40 for the tBMPI structure
  796.     Put #FileNum, , 0& ' reserved bytes
  797.     Put #FileNum, , 54& ' image offset from beginning of file
  798.     Put #FileNum, , tBMPI
  799.     Put #FileNum, , dibBits()
  800.     Close #FileNum
  801.     SaveToFile = True
  802.     
  803. ExitRoutine:
  804. If Not FileNum = 0 Then Close #FileNum
  805. If Err Then Err.Clear
  806.  
  807. End Function
  808.  
  809. Public Function SaveToStream(outStream() As Byte) As Boolean
  810.  
  811.     ' Should you want to serialize the 32bpp DIB
  812.     
  813.     On Error GoTo ExitRoutine   ' should out of memory occur?
  814.     
  815.     If m_Handle = 0 Then Exit Function
  816.     
  817.     Dim tBMPI As BITMAPINFO
  818.     
  819.     With tBMPI.bmiHeader
  820.         .biBitCount = 32
  821.         .biHeight = m_Height
  822.         .biPlanes = 1
  823.         .biSize = 40
  824.         .biSizeImage = m_Width * m_Height * 4&
  825.         .biWidth = m_Width
  826.     End With
  827.     
  828.     ReDim outStream(0 To 54 + tBMPI.bmiHeader.biSizeImage - 1)
  829.     
  830.     CopyMemory outStream(0), &H4D42, 2& ' bmp magic number
  831.     CopyMemory outStream(2), CLng(54 + tBMPI.bmiHeader.biSizeImage), 4& ' overall size of image
  832.     ' ^^ 54 = 14 byte bmp header + 40 for the tBMPI structure
  833.     CopyMemory outStream(10), 54&, 4& ' image offset from beginning of file
  834.     CopyMemory outStream(14), tBMPI, 40&
  835.     CopyMemory outStream(54), ByVal m_Pointer, tBMPI.bmiHeader.biSizeImage
  836.     
  837.     SaveToStream = True
  838.     
  839. ExitRoutine:
  840.     If Err Then
  841.         Err.Clear
  842.         Erase outStream()
  843.     End If
  844. End Function
  845.  
  846. Private Function HandleToStdPicture(ByVal hImage As Long, ByVal imgType As Long) As IPicture
  847.  
  848.     ' function creates a stdPicture object from a image handle (bitmap or icon)
  849.     ' Called by LoadPicture_ByHandle
  850.     
  851.     Dim lpPictDesc As PictDesc, aGUID(0 To 3) As Long
  852.     With lpPictDesc
  853.         .Size = Len(lpPictDesc)
  854.         .Type = imgType
  855.         .hHandle = hImage
  856.         .hPal = 0
  857.     End With
  858.     ' IPicture GUID {7BF80980-BF32-101A-8BBB-00AA00300CAB}
  859.     aGUID(0) = &H7BF80980
  860.     aGUID(1) = &H101ABF32
  861.     aGUID(2) = &HAA00BB8B
  862.     aGUID(3) = &HAB0C3000
  863.     ' create stdPicture
  864.     Call OleCreatePictureIndirect(lpPictDesc, aGUID(0), True, HandleToStdPicture)
  865.     
  866. End Function
  867.  
  868.  
  869. Private Sub Class_Initialize()
  870.  
  871.     ' Determine operating system for compatibility of 32bpp images
  872.     
  873.    Dim lValue As Long, osType As OSVERSIONINFOEX
  874.    ' Retrieve version data for OS.
  875.    osType.dwOSVersionInfoSize = Len(osType)
  876.    If GetVersionEx(osType) = 0 Then
  877.       ' The OSVERSIONINFOEX structure is only supported
  878.       ' in NT4/SP6+ and NT5.x, so we're likely running
  879.       ' on an earlier version of Windows. Revert structure
  880.       ' size to OSVERSIONINFO and try again.
  881.       osType.dwOSVersionInfoSize = Len(osType) - 14
  882.       Call GetVersionEx(osType)
  883.    End If
  884.    
  885.    ' Trim CSDVersion string at first null
  886.    lValue = InStr(osType.szCSDVersion, vbNullChar)
  887.    If lValue > 1 Then
  888.       osType.szCSDVersion = Left(osType.szCSDVersion, lValue - 1)
  889.    ElseIf lValue = 1 Then
  890.       osType.szCSDVersion = ""
  891.    End If
  892.    
  893.    If osType.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS Then
  894.         m_os9x = 1  ' this is a Win9x system
  895.          ' get LoWord of the build number
  896.         lValue = osType.dwBuildNumber
  897.         If (lValue And &HFFFF&) > &H7FFF Then
  898.             lValue = (lValue And &HFFFF&) - &H10000
  899.         Else
  900.             lValue = lValue And &HFFFF&
  901.         End If
  902.         If (osType.dwMinorVersion >= 10) Or _
  903.            (lValue >= 3000) Then m_os9x = m_os9x Or 2 ' win98 or ME
  904.     End If
  905.  
  906. End Sub
  907.  
  908. Private Sub Class_Terminate()
  909.     DestroyDIB ' simply clean up
  910. End Sub
  911.  
  912.  
  913.