cICOparser.cls
上传用户:jnjasmy
上传日期:2015-01-04
资源大小:637k
文件大小:35k
源码类别:

GDI/图象编程

开发平台:

Visual Basic

  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 = "cICOparser"
  10. Attribute VB_GlobalNameSpace = False
  11. Attribute VB_Creatable = True
  12. Attribute VB_PredeclaredId = False
  13. Attribute VB_Exposed = False
  14. Option Explicit
  15. ' No APIs are declared public. This is to prevent possibly, differently
  16. ' declared APIs, or different versions of the same API, from conflciting
  17. ' with any APIs you declared in your project. Same rule for UDTs.
  18. ' Note: I did take some liberties in several API declarations throughout
  19. ' Used for creating array overlays at other memory addresses
  20. Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (ByRef Ptr() As Any) As Long
  21. Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
  22. ' used to create images as needed
  23. Private Declare Function GetDC Lib "user32.dll" (ByVal hwnd As Long) As Long
  24. Private Declare Function ReleaseDC Lib "user32.dll" (ByVal hwnd As Long, ByVal hDC As Long) As Long
  25. Private Declare Function DeleteDC Lib "gdi32.dll" (ByVal hDC As Long) As Long
  26. Private Declare Function SelectObject Lib "gdi32.dll" (ByVal hDC As Long, ByVal hObject As Long) As Long
  27. Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long
  28. Private Declare Function CreateDIBitmap Lib "gdi32.dll" (ByVal hDC As Long, ByRef lpInfoHeader As BITMAPINFOHEADER, ByVal dwUsage As Long, ByRef lpInitBits As Any, ByRef lpInitInfo As Any, ByVal wUsage As Long) As Long
  29. 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 BITMAPINFO, ByVal wUsage As Long) As Long
  30. Private Declare Function GetGDIObject Lib "gdi32.dll" Alias "GetObjectA" (ByVal hObject As Long, ByVal nCount As Long, ByRef lpObject As Any) As Long
  31. Private Declare Function GetIconInfo Lib "user32.dll" (ByVal hIcon As Long, ByRef piconinfo As ICONINFO) As Long
  32. Private Declare Function DrawIcon Lib "user32.dll" (ByVal hDC As Long, ByVal x As Long, ByVal y As Long, ByVal hIcon As Long) As Long
  33. Private Type ICONINFO
  34.     fIcon As Long
  35.     xHotspot As Long
  36.     yHotspot As Long
  37.     hbmMask As Long
  38.     hbmColor As Long
  39. End Type
  40. Private Type BITMAP
  41.     bmType As Long
  42.     bmWidth As Long
  43.     bmHeight As Long
  44.     bmWidthBytes As Long
  45.     bmPlanes As Integer
  46.     bmBitsPixel As Integer
  47.     bmBits As Long
  48. End Type
  49. Private Type BITMAPINFOHEADER
  50.     biSize As Long
  51.     biWidth As Long         ' +4 from .biSize
  52.     biHeight As Long        ' +8
  53.     biPlanes As Integer     ' +12
  54.     biBitCount As Integer   ' +14
  55.     biCompression As Long   ' +16
  56.     biSizeImage As Long     ' +20
  57.     biXPelsPerMeter As Long ' +24
  58.     biYPelsPerMeter As Long ' +28
  59.     biClrUsed As Long       ' +32
  60.     biClrImportant As Long  ' 40th byte
  61. End Type
  62. Private Type BITMAPINFO
  63.     bmiHeader As BITMAPINFOHEADER
  64.     bmiPalette(0 To 255) As Long
  65. End Type
  66. Private Type SafeArrayBound
  67.     cElements As Long
  68.     lLbound As Long
  69. End Type
  70. Private Type SafeArray          ' used as DMA overlay on a DIB
  71.     cDims As Integer
  72.     fFeatures As Integer
  73.     cbElements As Long
  74.     cLocks As Long
  75.     pvData As Long
  76.     rgSABound(0 To 1) As SafeArrayBound ' reusable UDT for 1 & 2 dim arrays
  77. End Type
  78. Private Type ICONDIRENTRY
  79.     bWidth As Byte '// Width, in pixels, of the image
  80.     bHeight As Byte '// Height, in pixels, of the image
  81.     bColorCount As Byte '// Number of colors in image (0 if >=8bpp)
  82.     bReserved As Byte '// Reserved ( must be 0)
  83.     wPlanes As Integer '// Color Planes
  84.     wBitCount As Integer '// Bits per pixel
  85.     dwBytesInRes As Long '// How many bytes in this resource?
  86.     dwImageOffset As Long '// Where in the file is this image?
  87. End Type
  88. Private Type ICONDIR
  89.     idReserved As Integer '// Reserved (must be 0)
  90.     idType As Integer '// Resource Type (1 for icons)
  91.     idCount As Integer '// How many images?
  92.     idEntries() As ICONDIRENTRY '// An entry for each image (idCount of 'em)
  93. End Type
  94. Private Const png_Signature1 As Long = 1196314761   ' 1st 8 bytes of a PNG file start with these 8 bytes
  95. Private Const png_Signature2 As Long = 169478669
  96. Private m_icDirE() As ICONDIRENTRY  ' collection of icon directory entries
  97. Private m_icDir As ICONDIR          ' icon directory
  98. Private m_Bits() As Byte            ' icon bits
  99. Public Property Get Height(Index As Long) As Long
  100.     Height = m_icDirE(Index).bHeight    ' height of icon
  101.     If Height = 0& Then Height = 256&   ' 256x256 icons are identified as 0 in the icon structure
  102. End Property
  103. Public Property Get Width(Index As Long) As Long
  104.     Width = m_icDirE(Index).bHeight     ' width of icon
  105.     If Width = 0& Then Width = 256&     ' 256x256 icons are identified as 0 in the icon structure
  106. End Property
  107. Public Property Get IsIconPNG(Index As Long) As Boolean
  108.     IsIconPNG = m_icDirE(Index).wPlanes = 255   ' custom flag to distinguish PNG from icon
  109. End Property
  110. Public Property Get bitDepth(Index As Long) As Long
  111.     bitDepth = m_icDirE(Index).wBitCount    ' bit count/depth of icon
  112. End Property
  113. Public Property Get IconCount() As Long
  114.     IconCount = m_icDir.idCount
  115. End Property
  116. Public Property Get ColorCount(Index As Long) As Long
  117.     ' for paletted non-PNG images, number of colors that exist
  118.     ' This should be straight forward and is generally supplied in the icon entry's .bColorCount
  119.     ' member. But maybe .bColorCount may not be telling us the truth or it may be missing.
  120.     
  121.     ' To get the proper number supplied with the icon/bitmap, we will add the total bytes
  122.     ' used for the image & mask bytes, then add that to the bytes used for the header.
  123.     ' The difference/4 will always be correct.
  124.     Dim imageBits As Long, headerBits As Long
  125.     If m_icDirE(Index).wBitCount < 9 Then
  126.         imageBits = ColorByteCount(Index) + MaskByteCount(Index)
  127.         headerBits = m_Bits(m_icDirE(Index).dwImageOffset)
  128.         ColorCount = (m_icDirE(Index).dwBytesInRes - (imageBits + headerBits))  4&
  129.     End If
  130. End Property
  131. Public Property Get ColorByteOffset(Index As Long) As Long
  132.     ' Return the position in the source stream where the 1st byte of the color image
  133.     ' can be found; not called for PNGs
  134.     Dim Offset As Long
  135.     CopyMemory Offset, m_Bits(m_icDirE(Index).dwImageOffset), 4& ' header bytes
  136.     Offset = m_icDirE(Index).dwImageOffset + Offset ' shift offset to where icon structure begins
  137.     ' when image is paletted, the palette is included too
  138.     If m_icDirE(Index).wBitCount < 16 Then          ' get number of colors used in image
  139.         Offset = Offset + (2& ^ m_icDirE(Index).wBitCount) * 4& ' add that to the offset
  140.     End If
  141.     ColorByteOffset = Offset
  142. End Property
  143. Private Property Get MaskByteOffset(Index As Long) As Long
  144.     ' Return the position in the source stream where the 1st byte of the mask image
  145.     ' can be found; not called for PNGs. Here we work from the end of the icon structure
  146.     Dim Offset As Long
  147.     
  148.     ' Note: 32bpp icons have masks too
  149.     Offset = m_icDirE(Index).dwImageOffset + m_icDirE(Index).dwBytesInRes
  150.     Offset = Offset - MaskByteCount(Index)
  151.     MaskByteOffset = Offset
  152. End Property
  153. Private Property Get ColorByteCount(Index As Long) As Long
  154.     ' Return the number of image bytes used for the color image; not PNGs
  155.     ColorByteCount = iparseByteAlignOnWord(m_icDirE(Index).bWidth, m_icDirE(Index).wBitCount) * m_icDirE(Index).bHeight
  156. End Property
  157. Private Property Get MaskByteCount(Index As Long) As Long
  158.     ' Return the number of image bytes used for the mask image; not PNGs
  159.     MaskByteCount = iparseByteAlignOnWord(m_icDirE(Index).bWidth, 1&) * m_icDirE(Index).bHeight
  160. End Property
  161. Public Function LoadStream(inStream() As Byte, _
  162.                 ByVal desiredWidth As Long, ByVal desiredHeight As Long, _
  163.                 cHost As c32bppDIB, streamOffset As Long, streamLength As Long, _
  164.                 icoBitDepth As Long, Optional GlobalToken As Long) As Boolean
  165.     ' Purpose: Parse byte stream to determine if it is an icon file.
  166.     '   If it is an icon file, then select the best match for the passed
  167.     '   size and create our application's main image from the icon
  168.     ' Note: GIF, JPG, BMP, PNG & other formats have a magic number that
  169.     '   indicates what type of file it is. Icons/cursors do not; so we parse & error check
  170.     
  171.     ' Parameters:
  172.     ' inStream() :: an array of the icon file; can consist of more than one icon
  173.     ' desiredWidth :: width of icon to use, if available, else used for closest match
  174.     ' desiredHeight :: height of icon to use, if available, else used for closest match
  175.     ' cHost :: the application's image class
  176.     
  177.     ' IMPORTANT: the array offset & length are not checked in this class.
  178.     '   They were checked before this class was called. If this class is to
  179.     '   be pulled out and put in another project, ensure you include the
  180.     '   validation shown in c32bppDIB.LoadPicture_Stream
  181.     
  182.     Dim icEntry As Long, icValue As Long
  183.     Dim icPtr As Long, icBytesNeed As Long
  184.     Dim bIconFile As Boolean
  185.     
  186.     Dim tDC As Long, hDib As Long, dDC As Long, hObj As Long
  187.     Dim tSA As SafeArray
  188.     Dim tBMPI As BITMAPINFO
  189.     Dim cPNG As cPNGparser
  190.     
  191.     With tSA                    ' overlay the passed stream with our module-level array
  192.         .cbElements = 1         ' as byte
  193.         .cDims = 1              ' as 1 dimensional
  194.         .pvData = VarPtr(inStream(LBound(inStream)))
  195.         If streamLength = 0 Then streamLength = UBound(inStream) + 1
  196.         .rgSABound(0).cElements = streamLength
  197.     End With
  198.     CopyMemory ByVal VarPtrArray(m_Bits), VarPtr(tSA), 4& ' establish overlay
  199.     
  200.     icBytesNeed = 6&                        ' length of the ICONDIRECTORY
  201.     If icPtr + icBytesNeed <= streamLength Then    ' ensure enough bytes exist
  202.         bIconFile = True                    ' good, let's continue
  203.         ' cache the ICONDIRECTORY
  204.         CopyMemory m_icDir.idReserved, m_Bits(icPtr), icBytesNeed
  205.         If m_icDir.idCount < 1 Then         ' no icons or not an icon file
  206.             bIconFile = False
  207.         ElseIf Not m_icDir.idReserved = 0 Then  ' per MSDN, must be zero
  208.             bIconFile = False
  209.         ElseIf m_icDir.idType < 1 Or m_icDir.idType > 2 Then
  210.             bIconFile = False               ' per MSDN, must be 1 or 2 (1=icon,2=cursor)
  211.         Else
  212.             icPtr = icPtr + icBytesNeed     ' move array pointer
  213.             icBytesNeed = 16&               ' length of directory entry
  214.             If icPtr + icBytesNeed * m_icDir.idCount > streamLength Then
  215.                 bIconFile = False           ' not enough bytes for expected entries
  216.             Else
  217.                 ReDim m_icDirE(1 To m_icDir.idCount)        ' size our entries
  218.                 icBytesNeed = m_icDir.idCount * icBytesNeed ' & cache them
  219.                 CopyMemory m_icDirE(1).bWidth, m_Bits(icPtr), icBytesNeed
  220.                 icBytesNeed = icBytesNeed + 6&       ' move array pointer
  221.                 For icEntry = 1 To m_icDir.idCount
  222.                     ' each entry indicates how many bytes are used for it.
  223.                     ' total the bytes and ensure enough bytes exist
  224.                     icBytesNeed = icBytesNeed + m_icDirE(icEntry).dwBytesInRes
  225.                 Next
  226.                 If icBytesNeed > streamLength Then bIconFile = False ' not enough bytes
  227.             End If
  228.         End If
  229.     End If
  230.     
  231.     If bIconFile Then
  232.     
  233.         ' Through experience, I have found the bitcount of the icons
  234.         ' contained within the IconDirectoryEntry structures may be
  235.         ' wrong or may not be filled in. Here, we will erase them & fill
  236.         ' them in from the bitmap info headers that exist in the array.
  237.         icBytesNeed = 0&
  238.         For icEntry = 1 To m_icDir.idCount
  239.             m_icDirE(icEntry).wPlanes = 1 ' not required, but used as a flag internally as indicating valid or invalid image
  240.             ' get bitcount from the bitmap header
  241.             CopyMemory icBytesNeed, m_Bits(m_icDirE(icEntry).dwImageOffset + 14), 2&
  242.             
  243.             If icBytesNeed = 0 Then ' if it is zero (shouldn't be); use the bitcount from the icon entry structure
  244.                 ' ensure the icon entry bitcount is not zero...
  245.                 If m_icDirE(icEntry).wBitCount = 0 Then
  246.                     bIconFile = False
  247.                 Else
  248.                     CopyMemory m_Bits(m_icDirE(icEntry).dwImageOffset + 14), m_icDirE(icEntry).wBitCount, 2&
  249.                 End If
  250.                 
  251.             ElseIf icBytesNeed = 21060& Then
  252.                 ' flag for PNG, double check & abort if not -- then it is not an icon file
  253.                 bIconFile = ParsePNGheader(icEntry)
  254.                 
  255.             Else    ' use the bitcount from the bitmap header
  256.                 m_icDirE(icEntry).wBitCount = icBytesNeed
  257.             End If
  258.         Next
  259.     
  260.         If bIconFile = True Then
  261.             LoadStream = True
  262.             If Not cHost Is Nothing Then
  263.             
  264.                 ' appears we have a valid icon file. Find closest match for requested size
  265.                 If desiredWidth < 1 Then desiredWidth = 32& ' default if none provided
  266.                 If desiredHeight < 1 Then desiredHeight = 32&
  267.                 icEntry = GetBestMatch(desiredWidth, desiredHeight, icoBitDepth)
  268.                 
  269.                 If Not icEntry = 0 Then ' else something is wrong with the icon structure(s) in this file
  270.                 
  271.                     If IsIconPNG(icEntry) Then ' png flag
  272.                         
  273.                         ' we need to pass this off to a PNG class for parsing/processing
  274.                         Set cPNG = New cPNGparser
  275.                         LoadStream = cPNG.LoadStream(inStream, cHost, m_icDirE(icEntry).dwImageOffset, m_icDirE(icEntry).dwBytesInRes, GlobalToken)
  276.                         Set cPNG = Nothing
  277.                         If Not cHost.Handle = 0& Then cHost.ImageType = imgPNGicon
  278.                         
  279.                     Else
  280.                         ' create the main application's image, blank.
  281.                         cHost.InitializeDIB Width(icEntry), Height(icEntry)
  282.                         
  283.                         ' copy the bitmap information header and fix it. Per MSDN, not all
  284.                         ' members of the header are required to be filled in. We need them.
  285.                         CopyMemory tBMPI.bmiHeader, m_Bits(m_icDirE(icEntry).dwImageOffset), 40&
  286.                         With tBMPI.bmiHeader
  287.                             .biClrUsed = ColorCount(icEntry)    ' fix when bitcount <= 8bpp
  288.                             .biHeight = Height(icEntry)         ' height is doubled; fix it
  289.                             .biSizeImage = 0                    ' erase; don't need this
  290.                             .biXPelsPerMeter = 0                ' erase; don't need this
  291.                             .biYPelsPerMeter = 0                ' erase; don't need this
  292.                         End With
  293.                         ' copy the fixed header back into the array
  294.                         CopyMemory m_Bits(m_icDirE(icEntry).dwImageOffset), tBMPI.bmiHeader, 40&
  295.                         
  296.                         ' the next part of the routine is to create a compatible bitmap using
  297.                         ' maximum screen colors on the system.  We will use the API to create it
  298.                         ' for us from the bitmap header we just tweaked above. Otherwise we would
  299.                         ' have to parse the bits ourselves, bloating code to handle 7 possible bit
  300.                         ' depths in combination with several compression algorithms & various RGB masks.
  301.                         tDC = GetDC(0&)
  302.                         hDib = CreateDIBitmap(tDC, tBMPI.bmiHeader, 4, m_Bits(ColorByteOffset(icEntry)), m_Bits(m_icDirE(icEntry).dwImageOffset), 0&)
  303.                         
  304.                         If hDib = 0& Then
  305.                             ReleaseDC 0&, tDC
  306.                             cHost.DestroyDIB
  307.                             ' major problem here; the icon contained in the stream appears to be faulty
  308.                             ' we can't use it. Abort.
  309.                         Else
  310.                             ' here we are defining our application's image.
  311.                             With tBMPI.bmiHeader
  312.                                 .biSize = 40&
  313.                                 .biBitCount = 32            ' 32bpp
  314.                                 .biHeight = cHost.Height    ' same width & height
  315.                                 .biWidth = cHost.Width      ' of the source image
  316.                                 .biPlanes = 1
  317.                                 .biSizeImage = .biHeight * .biWidth * 4&
  318.                             End With
  319.                             ' transfer the image bits from the bitmap created from the array to
  320.                             ' our application's image
  321.                             GetDIBits tDC, hDib, 0&, cHost.Height, ByVal cHost.BitsPointer, tBMPI, 0&
  322.                             ReleaseDC 0&, tDC               ' release dc; don't need it any longer
  323.                             DeleteObject hDib               ' kill the source bitmap; not needed
  324.                             ApplyAlphaMask icEntry, cHost   ' add the alpha channel to app's image
  325.                         End If
  326.                     End If
  327.                 End If
  328.             End If
  329.         End If
  330.     End If
  331.     CopyMemory ByVal VarPtrArray(m_Bits), 0&, 4&    ' remove overlay
  332.     
  333. End Function
  334. Public Function ConvertstdPicTo32bpp(Handle As Long, cHost As c32bppDIB) As Boolean
  335.     ' Purpose: Convert a single icon from a stdPicture or handle to a 32bpp bitmap
  336.     If Handle = 0& Then Exit Function
  337.     
  338.     Dim tSA As SafeArray
  339.     Dim icoInfo As ICONINFO, tBMPI As BITMAPINFO
  340.     Dim tBMPc As BITMAPINFO, tBMPm As BITMAPINFO
  341.     Dim tDC As Long, hostDC As Long
  342.     Dim x As Long, y As Long
  343.     
  344.     ' see if we can get the icon information
  345.     If GetIconInfo(Handle, icoInfo) = 0& Then Exit Function
  346.     
  347.     m_icDir.idCount = 1
  348.     m_icDir.idType = icoInfo.fIcon ' 0=icon, 1=cursor
  349.     ReDim m_icDirE(1 To 1)  ' we will have 1 entry
  350.     
  351.     tDC = GetDC(0&)
  352.     
  353.     If Not icoInfo.hbmColor = 0& Then    ' do we have a color image? no for B&W
  354.         tBMPc.bmiHeader.biSize = 40    ' let's fill in the BitmapInfo header
  355.         If GetDIBits(tDC, icoInfo.hbmColor, 0&, 0&, ByVal 0&, tBMPc, 0&) = 0& Then
  356.             m_icDir.idCount = 0 ' oops; something critical happened
  357.         Else
  358.             With tBMPI.bmiHeader    ' now fill in our destination description
  359.                 .biBitCount = 32
  360.                 .biHeight = tBMPc.bmiHeader.biHeight
  361.                 .biWidth = tBMPc.bmiHeader.biWidth
  362.                 .biPlanes = 1
  363.                 .biSize = 40&
  364.                 cHost.InitializeDIB .biWidth, .biHeight ' setup destination DIB
  365.             End With
  366.             ' use API again, to pass the bits from the color icon image to our DIB
  367.             GetDIBits tDC, icoInfo.hbmColor, 0&, tBMPc.bmiHeader.biHeight, ByVal cHost.BitsPointer, tBMPI, 0&
  368.         
  369.             ' we will ensure the passed icon is not already a 32bpp ARGB image
  370.             ' stdPictures won't be this way, but a call to LoadIconFromFile API can load XP icons
  371.             With tSA
  372.                 .cbElements = 1
  373.                 .cDims = 2
  374.                 .pvData = cHost.BitsPointer
  375.                 .rgSABound(0).cElements = cHost.Height
  376.                 .rgSABound(1).cElements = cHost.scanWidth
  377.             End With
  378.             CopyMemory ByVal VarPtrArray(m_Bits), VarPtr(tSA), 4&
  379.             m_icDirE(1).wBitCount = 1
  380.             For y = 0 To cHost.Height - 1
  381.                 For x = 3 To cHost.scanWidth - 1 Step 4
  382.                     If Not m_Bits(x, y) = 0 Then
  383.                         m_icDirE(1).wBitCount = 32  ' looking for any non-zero alpha byte
  384.                         y = cHost.Height            ' force outer loop to terminate
  385.                         Exit For
  386.                     End If
  387.                 Next
  388.             Next
  389.             If m_icDirE(1).wBitCount = 32 Then
  390.                 ' premultiply DIB as needed & set host imagetype, alpha properties
  391.                 iparseValidateAlphaChannel m_Bits, True, True, 0&
  392.                 If m_icDir.idType = 1 Then cHost.ImageType = imgIconARGB Else cHost.ImageType = imgCursorARGB
  393.                 cHost.Alpha = True
  394.             End If
  395.             CopyMemory ByVal VarPtrArray(m_Bits), 0&, 4&
  396.         End If
  397.     Else
  398.         m_icDirE(1).wBitCount = 1   ' b&w icon/cursor
  399.     End If
  400.     
  401.     ' pretty much same thing for the B&W, 1bpp mask
  402.     ' Valid icons always have a mask, so no need to check .hbmMask=0 since this
  403.     ' icon exists in a stdPicture and that, in itself, validated the icon for us
  404.     If m_icDirE(1).wBitCount = 1 Then   ' else already processed as 32bpp icon/cursor
  405.         If m_icDir.idCount = 1 Then
  406.             tBMPm.bmiHeader.biSize = 40&
  407.             If GetDIBits(tDC, icoInfo.hbmMask, 0, 0, ByVal 0&, tBMPm, 0) = 0 Then
  408.                 m_icDir.idCount = 0 ' oops; something critical happened
  409.             Else
  410.                 With tBMPI.bmiHeader
  411.                     If icoInfo.hbmColor = 0& Then
  412.                         ' we have a b&w icon
  413.                         .biBitCount = 32
  414.                         .biHeight = tBMPm.bmiHeader.biHeight  2
  415.                         .biWidth = tBMPm.bmiHeader.biWidth
  416.                         .biPlanes = 1
  417.                         .biSize = 40&
  418.                         ' render the icon onto our dib
  419.                         ' Note: in IDE, icon/cursor will be b&w, but when compiled
  420.                         ' if the cursor had colors, the colors will be shown
  421.                         cHost.InitializeDIB .biWidth, .biHeight  ' setup destination DIB
  422.                         hostDC = cHost.LoadDIBinDC(True)
  423.                         DrawIcon hostDC, 0, 0, Handle
  424.                         cHost.LoadDIBinDC False
  425.                         
  426.                     End If
  427.                     ' size our local array to hold the mask bits; these will be used
  428.                     ' to tweak the 32bpp DIB's alpha channel in ApplyAlphaMask
  429.                     ReDim m_Bits(0 To iparseByteAlignOnWord(1, .biWidth) * .biHeight - 1&)
  430.                 End With
  431.                 
  432.                 ' prepare bitmap info for our 1bpp mask array
  433.                 tBMPI.bmiPalette(1) = vbWhite
  434.                 With tBMPI.bmiHeader
  435.                     .biBitCount = 1
  436.                     .biClrUsed = 2
  437.                 End With
  438.                 ' use API again to pass the 1bpp image to our array
  439.                 GetDIBits tDC, icoInfo.hbmMask, 0&, tBMPI.bmiHeader.biHeight, m_Bits(0), tBMPI, 0&
  440.                 
  441.                 ' fill in the icon entry structure
  442.                 With m_icDirE(1)
  443.                     .bHeight = tBMPI.bmiHeader.biHeight
  444.                     .bWidth = tBMPm.bmiHeader.biWidth
  445.                     .dwBytesInRes = UBound(m_Bits) + 1& ' we only have a mask in our array
  446.                     .wBitCount = 1  ' the bitmap retrieved from the icon/cursor can be 32bpp
  447.                     .wPlanes = 1    ' so we force the ApplyAlphaMask to use the 1bpp parsing routine
  448.                 End With
  449.             
  450.             End If
  451.         End If
  452.     End If
  453.     ReleaseDC 0&, tDC
  454.     
  455.     ' clean up; GetIconInfo creates up to 2 bitmaps we must destroy
  456.     If Not icoInfo.hbmColor = 0& Then DeleteObject icoInfo.hbmColor
  457.     If Not icoInfo.hbmMask = 0& Then DeleteObject icoInfo.hbmMask
  458.     
  459.     If m_icDir.idCount = 1 Then           ' no errors encountered
  460.         If m_icDirE(1).wBitCount = 1 Then ' now apply the mask
  461.             ApplyAlphaMask 1&, cHost
  462.             Erase m_Bits()
  463.         End If
  464.         ConvertstdPicTo32bpp = True
  465.     End If
  466.     
  467. End Function
  468. Private Sub ApplyAlphaMask(Index As Long, cHost As c32bppDIB)
  469.     ' Purpose: Either blend or simulate transparency for icons
  470.     ' The primary DIB for this application is 32bpp. Icons may or
  471.     '   may not be 32bpp. When 32bpp, the icon RGB values are not
  472.     '   pre-multiplied; so we need to pre-multiply them.  When
  473.     '   the icon is not 32bpp, then it may have transparency,
  474.     '   and we will modify our 32bpp image to identify which
  475.     '   pixels are transparent and which are not.
  476.     Dim dX As Long, x As Long, y As Long, m As Long
  477.     Dim aDIB() As Byte
  478.     Dim Pow2(0 To 7) As Long
  479.     Dim maskShift As Long, maskPtr As Long
  480.     Dim maskScanWidth As Long, maskOffset As Long
  481.     Dim bAlpha As Boolean
  482.     
  483.     Dim tSA As SafeArray
  484.     With tSA                ' overlay the 32bpp dib
  485.         .cbElements = 1     ' as bytes
  486.         .cDims = 2          ' as 2D array
  487.         .pvData = cHost.BitsPointer
  488.         .rgSABound(0).cElements = cHost.Height
  489.         .rgSABound(1).cElements = cHost.scanWidth
  490.     End With
  491.     CopyMemory ByVal VarPtrArray(aDIB), VarPtr(tSA), 4& ' establish overlay
  492.     
  493.     ' separate routines for 32bpp images and non-32bpp images.
  494.     ' 32bpp images have the alpha mask combined with the RGB values. The
  495.     ' transparency mask also exists but won't be used for 32bpp images.
  496.     
  497.     If m_icDirE(Index).wBitCount = 32 Then      ' alphablended icon
  498.                                                 ' get location of 1st color byte
  499.         maskPtr = ColorByteOffset(Index) + 3&   ' then move to the alpha byte
  500.         For y = 0& To cHost.Height - 1&         ' loop thru scan lines
  501.             For x = 0& To cHost.scanWidth - 1& Step 4&
  502.                 Select Case m_Bits(maskPtr)
  503.                 Case 0          ' 100% transparent
  504.                     CopyMemory aDIB(x, y), 0&, 4&
  505.                 Case 255        ' 100% opaque
  506.                     aDIB(x + 3, y) = 255
  507.                 Case Else       ' blend; calculation from MSDN
  508.                     For dX = x To x + 2&
  509.                         aDIB(dX, y) = ((0& + m_Bits(maskPtr)) * aDIB(dX, y))  &HFF
  510.                     Next
  511.                     aDIB(dX, y) = m_Bits(maskPtr) ' keep the alpha byte value
  512.                 End Select
  513.                 maskPtr = maskPtr + 4&  ' move mask pointer to next alpha byte
  514.             Next
  515.         Next
  516.         If m_icDir.idType = 1 Then cHost.ImageType = imgIconARGB Else cHost.ImageType = imgCursorARGB
  517.         bAlpha = True
  518.         
  519.     Else    ' 1,2,4,8,16,24 bpp images - not alpha blended, no alph-channel
  520.         
  521.         Pow2(0) = 1&     ' build a power of two lookup table to parse the 1bpp mask
  522.         For x = 1& To UBound(Pow2)
  523.             Pow2(x) = Pow2(x - 1&) * 2&
  524.         Next
  525.         maskOffset = MaskByteOffset(Index)  ' location where mask starts
  526.         maskScanWidth = iparseByteAlignOnWord(cHost.Width, 1) ' how many mask bytes per scan line
  527.         For y = 0& To cHost.Height - 1&     ' loop thru the scan lines
  528.             maskPtr = y * maskScanWidth + maskOffset  ' adjust mask pointer per scan line
  529.             maskShift = 7&                            ' bit position of mask
  530.             dX = 3&
  531.             ' note: do not loop thru using maskScanWidth. If the icon is a custom
  532.             ' icon that has no DWORD aligned width, you will overflow the target
  533.             ' DIB width and eventually write to uninitialized memory
  534.             For x = 1& To cHost.Width
  535.                 If (m_Bits(maskPtr) And Pow2(maskShift)) = 0 Then ' is pixel transparent?
  536.                     aDIB(dX, y) = 255        ' nope, make it 100% opaque
  537.                 Else                        ' else make it 100% transparent
  538.                     CopyMemory aDIB(dX - 3&, y), 0&, 4&
  539.                     bAlpha = True
  540.                 End If
  541.                 If maskShift = 0& Then   ' when we get to zero, the mask byte is read
  542.                     maskShift = 7&       ' reset for next mask byte
  543.                     maskPtr = maskPtr + 1& ' move to next maskb byte
  544.                 Else
  545.                     maskShift = maskShift - 1& ' adjust mask shifter
  546.                 End If
  547.                 dX = dX + 4&             ' move the 32bpp pointer along
  548.             Next
  549.         Next
  550.         If m_icDir.idType = 1 Then cHost.ImageType = imgIcon Else cHost.ImageType = imgCursor
  551.     End If
  552.     CopyMemory ByVal VarPtrArray(aDIB), 0&, 4&  ' remove overlay
  553.     cHost.Alpha = bAlpha
  554.     
  555. End Sub
  556. Private Function GetBestMatch(cX As Long, cY As Long, icoBitDepth As Long) As Long
  557.     ' Purpose: Find the nearest match to the passed Size.
  558.     
  559.     ' Note that this routine is weighted for monitors set at 32bit.
  560.     ' If this is not acceptable, then algorithm slightly
  561.     '   from adding weight of:  Abs(32 - bitDepth(icEntry))
  562.     '   to adding weight of: Abs([ScreenColorDepth] - bitDepth(icEntry))
  563.     
  564.     ' additionally, the weighting is customized to favor larger icons over smaller ones
  565.     ' when stretching would be needed. The thought is that stretching down almost always
  566.     ' produces better quality graphics than stretching up.
  567.     Dim Weights() As Long
  568.     Dim icEntry As Long, bestMatch As Long
  569.     Dim lWeight As Long
  570.     
  571.     If m_icDir.idCount > 1 Then ' more than one icon?
  572.     
  573.         ReDim Weights(-1 To m_icDir.idCount)
  574.         ' set least desirable weight: some large number
  575.         Weights(0) = 10000&
  576.         
  577.         For icEntry = 1 To m_icDir.idCount
  578.             ' simple weight; use the difference between desired size & icon size
  579.             If Not m_icDirE(icEntry).wBitCount = 0 Then     ' if a image within icon file is faulty, we ignore it
  580.                 
  581.                 lWeight = Width(icEntry) - cX ' & penalize if stretching larger is needed
  582.                 If cX > Width(icEntry) Then lWeight = lWeight * 2&
  583.                 Weights(icEntry) = lWeight
  584.                 
  585.                 lWeight = Height(icEntry) - cY ' & penalize if stretching larger is needed
  586.                 If cY > Height(icEntry) Then lWeight = lWeight * 2&
  587.                 Weights(icEntry) = Weights(icEntry) + lWeight
  588.                 
  589.                 ' add the weight for bit depth
  590.                 Weights(icEntry) = Weights(icEntry) + Abs(icoBitDepth - bitDepth(icEntry))
  591.                 
  592.                 If m_icDirE(icEntry).wBitCount > 32 Then Weights(icEntry) = -10000&  ' if future icons are something like 48bpp
  593.                 
  594.                 ' compare; one with lowest value wins
  595.                 If Weights(icEntry) = 0 Then
  596.                     bestMatch = icEntry
  597.                     Exit For
  598.                 ElseIf Weights(icEntry) < Weights(0) Then
  599.                     If Weights(icEntry) > 0 Then        ' basically rejects icons that need to be stretched up
  600.                         Weights(0) = Weights(icEntry)
  601.                         bestMatch = icEntry
  602.                     End If
  603.                 End If
  604.             End If
  605.         Next
  606.         If bestMatch = 0 Then ' every image is too small and must be stretched. We will get the highest negative value now
  607.             For icEntry = icEntry - 1& To 1& Step -1&
  608.                 Weights(icEntry) = Abs(Weights(icEntry)) + Abs(32& - bitDepth(icEntry))
  609.                 If Weights(icEntry) < Weights(0) Then
  610.                     Weights(0) = Weights(icEntry)
  611.                     bestMatch = icEntry
  612.                 End If
  613.                 If bestMatch = 0& Then bestMatch = 1&
  614.             Next
  615.         End If
  616.         
  617.     Else ' only one icon/PNG
  618.         If m_icDirE(1).wBitCount = 0 Then bestMatch = 0& Else bestMatch = 1&
  619.     
  620.     End If
  621.     GetBestMatch = bestMatch
  622.     
  623. End Function
  624. Private Function ParsePNGheader(Index As Long) As Boolean
  625.     ' PNG's IHDR structure
  626.     '    Width As Long              << cannot be negative
  627.     '    Height As Long             << cannot be negative
  628.     '    BitDepth As Byte           << must be 1,2,4,8,16
  629.     '    ColorType As Byte          << must be 0,2,3,4,6
  630.     '    Compression As Byte        << must be zero
  631.     '    Filter As Byte             << must be zero
  632.     '    Interlacing As Byte        << must be zero or one
  633.     
  634.     Dim lValue As Long, Offset As Long
  635.     Const chnk_IHDR As Long = &H52444849 'Image header PNG flag
  636.     
  637.     On Error GoTo ExitRoutine:
  638.     ' get the image width; the value will be a reversed long
  639.     With m_icDirE(Index)
  640.         
  641.         .wPlanes = 255 ' flag for png
  642.         
  643.         ' verify this is a png signture
  644.         CopyMemory lValue, m_Bits(m_icDirE(Index).dwImageOffset), 4&
  645.         If lValue = png_Signature1 Then ' probably a png (Vista Icon)
  646.             ' the 1st 4 bytes were verified, very next 4 bytes
  647.             CopyMemory lValue, m_Bits(m_icDirE(Index).dwImageOffset + 4), 4&
  648.             If lValue = png_Signature2 Then  ' definitely a png (Vista Icon)
  649.         
  650.             ' If this is a valid PNG, the next 4 bytes would be 13 (size of header)
  651.             ' and the following 4 bytes would be the header name (chnk_IHDR)
  652.             CopyMemory lValue, m_Bits(.dwImageOffset + 12&), 4&
  653.             
  654.                 If lValue = chnk_IHDR Then
  655.                 
  656.                     ' get PNG's width
  657.                     CopyMemory lValue, m_Bits(.dwImageOffset + 16&), 4&
  658.                     lValue = iparseReverseLong(lValue)
  659.                     Select Case lValue
  660.                         Case 256: .bWidth = 0&
  661.                         Case 1 To 255: .bWidth = lValue
  662.                         Case Else: .wBitCount = 0& ' prevent processing PNG as an option
  663.                     End Select
  664.                     
  665.                     ' do the same for the height
  666.                     CopyMemory lValue, m_Bits(.dwImageOffset + 20&), 4&
  667.                     lValue = iparseReverseLong(lValue)
  668.                     Select Case lValue
  669.                         Case 256: .bHeight = 0&
  670.                         Case 1 To 255: .bHeight = lValue
  671.                         Case Else: .wBitCount = 0& ' prevent processing PNG as an option
  672.                     End Select
  673.                 
  674.                     If .wBitCount = 0 Then
  675.                         .wBitCount = m_Bits(.dwImageOffset + 24&)
  676.                         If .wBitCount = 16 Then
  677.                             .wBitCount = 32 ' for our purposes a 48bpp image is a 32bpp image
  678.                             
  679.                         ElseIf Not .wBitCount = 0 Then
  680.                             Select Case m_Bits(.dwImageOffset + 25&)
  681.                             Case 4, 6: .wBitCount = 32  ' alpha png
  682.                             Case 2: .wBitCount = 24     ' true color
  683.                             Case Else                   ' no change in interpretation
  684.                             End Select
  685.                         End If
  686.                     End If
  687.                     
  688.                     ' the remaining bytes of the IHDR are not needed for the icon class
  689.                     ParsePNGheader = (.wBitCount > 0)
  690.             
  691.                 End If
  692.             End If
  693.         End If
  694.     End With
  695. ExitRoutine:
  696. If Err Then
  697.     Err.Clear
  698.     m_icDirE(Index).wBitCount = 0
  699. End If
  700. End Function