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

GDI/图象编程

开发平台:

Visual Basic

  1.     CopyMemory ByVal VarPtrArray(dibBytes), VarPtr(tSA), 4&
  2.     For Y = 0& To m_Height - 1&
  3.         For X = 3& To m_Width * 4& - 1& Step 4&
  4.             dibBytes(X, Y) = 255
  5.         Next
  6.     Next
  7.     CopyMemory ByVal VarPtrArray(dibBytes), 0&, 4&
  8.     m_AlphaImage = False            ' we are not using transparency
  9.     m_Format = imgCheckerBoard      ' special flag for user
  10.     
  11.     CreateCheckerBoard = True
  12. End Function
  13. Private Function RotateImage(ByVal hDC As Long, ByVal Angle As Single, _
  14.                             ByVal TopX As Long, ByVal TopY As Long, _
  15.                             ByVal destWidth As Long, ByVal destHeight As Long, _
  16.                             ByVal SrcX As Long, ByVal SrcY As Long, _
  17.                             ByVal srcWidth As Long, ByVal srcHeight As Long, _
  18.                             ByVal Opacity As Long, _
  19.                             ByRef destHostDIB As c32bppDIB, _
  20.                             ByVal grayScale As eGrayScaleFormulas, _
  21.                             ByVal LightAdjustment As Single) As Boolean
  22.     ' Internal function will rotate an image by passed Angle and render to the passed hDC.
  23.     ' This function simultaneously rotates, scales and then blends.
  24.     
  25.     ' Note: Me.HighQualityInterpolation property setting is used to determine quality of rotation/scaling
  26.     ' Called only by the Render method
  27.     
  28.     
  29.     ' first see if we can do this via GDI+
  30.     If Me.isGDIplusEnabled Then
  31.         Dim cGDIp As New cGDIPlus
  32.         If cGDIp.RenderGDIplus(Me, hDC, Angle, Opacity, TopX, TopY, destWidth, destHeight, SrcX, SrcY, srcWidth, srcHeight, m_StretchQuality, grayScale, m_GDItoken, LightAdjustment) = True Then
  33.             RotateImage = True
  34.             Exit Function
  35.         End If
  36.         Set cGDIp = Nothing
  37.     End If
  38.         
  39.     Dim cosTx As Double, sinTx As Double
  40.     Dim cosTy As Double, sinTy As Double
  41.     Dim scalerX As Double, scalerY As Double
  42.     
  43.     Dim maxX As Long, maxY As Long, maxSize As Long
  44.     Dim ctrX As Long, ctrY As Long
  45.     Dim xOffset As Double, yOffset As Double
  46.     Dim targetX As Double, targetY As Double
  47.     
  48.     Dim dSA As SafeArray, sSA As SafeArray
  49.     Dim dBytes() As Byte, sBytes() As Byte
  50.     
  51.     Dim rHost As New c32bppDIB
  52.     Dim lRow As Long, lCol As Long
  53.     
  54.     ' following variables are used for the BiLinear interpolation only
  55.     Dim tgtY As Long, tgtX As Long
  56.     Dim srcPixel As Long, dstPixel As Long, srcRow As Long
  57.     Dim edgeOffsetX As Long, edgeOffsetY As Long
  58.     Dim fY As Double, fX As Double, iX As Long, iY As Long
  59.     Dim R As Double, G As Double, B As Double, A As Double
  60.     
  61.     ' handle mirroring as needed. Fix negative values as needed
  62.     If destWidth < 0& Then TopX = TopX + destWidth
  63.     If destHeight < 0& Then TopY = TopY + destHeight
  64.     If MirrorDIB(SrcX, SrcY, srcWidth, srcHeight, destWidth, destHeight, sBytes(), , LightAdjustment) = False Then
  65.         ' if light adjustments, preprocess bytes
  66.         If Not LightAdjustment = 0! Then Call LightenDarken(Me, LightAdjustment, sBytes)
  67.     End If
  68.     
  69.     ' determine the scale to use based off the passed
  70.     ' source and destination widths,heights
  71.     scalerX = destWidth / srcWidth      ' scale x coordinates
  72.     scalerY = destHeight / srcHeight    ' scale y coordinates
  73.     
  74.     ' convert angle to radians & calculate scaled COS/SIN of the angle
  75.     ' Multiplying by Negative so we rotate clockwise
  76.     sinTx = -((Angle Mod 360) * (4& * Atn(1))) / 180  ' convert Degree to Radian
  77.     cosTy = Cos(sinTx) / scalerY      ' get cosine of angle (Y coordinates)
  78.     sinTy = Sin(sinTx) / scalerY      ' get sine of angle (Y coordinates)
  79.     
  80.     cosTx = Cos(sinTx) / scalerX      ' get cosine of angle (X coordinates)
  81.     sinTx = Sin(sinTx) / scalerX      ' get sine of angle  (X coordinates)
  82.     ' determine maximum size image we will need to cover any angle
  83.     maxSize = Sqr(destWidth * destWidth + destHeight * destHeight)
  84.     
  85.     ' create a temporary DIB to hold the rotated image
  86.     rHost.InitializeDIB maxSize, maxSize
  87.     rHost.isGDIplusEnabled = Me.isGDIplusEnabled
  88.     rHost.HighQualityInterpolation = Me.HighQualityInterpolation
  89.     
  90.     On Error GoTo eh
  91.     ' overlay the temp DIB and our host DIB
  92.     With dSA
  93.         .cbElements = 1
  94.         .cDims = 2
  95.         .pvData = rHost.BitsPointer
  96.         .rgSABound(0).cElements = maxSize
  97.         .rgSABound(1).cElements = maxSize * 4&
  98.     End With
  99.     CopyMemory ByVal VarPtrArray(dBytes), VarPtr(dSA), 4&
  100.     
  101.     If iparseIsArrayEmpty(VarPtrArray(sBytes)) = 0& Then
  102.         With sSA
  103.             .cbElements = 1
  104.             .cDims = 2
  105.             .pvData = m_Pointer
  106.             .rgSABound(0).cElements = m_Height
  107.             .rgSABound(1).cElements = m_Width * 4&
  108.         End With
  109.         CopyMemory ByVal VarPtrArray(sBytes), VarPtr(sSA), 4&
  110.     End If
  111.     
  112.     ' bottom up dib, vertical offset is from bottom, not top
  113.     SrcY = UBound(sBytes, 2) - srcHeight - SrcY + 1&
  114.     ' set up offsets for calculating rotated/scaled points
  115.     maxX = srcWidth + SrcX      ' the right edge of the source image
  116.     maxY = srcHeight + SrcY     ' the bottom edge of the source image
  117.     ' determine where the center of the source selected bounds falls within the maxSize bounds
  118.     ctrX = srcWidth  2 + SrcX  ' the center of the source image
  119.     ctrY = srcHeight  2 + SrcY
  120.     ' calculate offsets to "center" destination image in its maxSize window
  121.     xOffset = ((maxSize - srcWidth)  2)
  122.     yOffset = ((maxSize - srcHeight)  2)
  123.     
  124.     ' here's where we apply all of the above offsets
  125.     ' This is a bit complicated because we allow any angle rotation,
  126.     ' and also allowing portions of the image or entire image to be rotated
  127.     ' and also allows scaling up or down
  128.     If m_StretchQuality = False Then
  129.     
  130.         For lRow = -yOffset To maxSize - yOffset - 1&
  131.             
  132.             ' Calculate the point in the source image needed for the rotated point in destination image
  133.             ' This only needs to be done once per image scan line & contains many math executions
  134.             targetX = (-xOffset - ctrX) * cosTx + (lRow - ctrY) * sinTx + ctrX
  135.             targetY = (lRow - ctrY) * cosTy - (-xOffset - ctrX) * sinTy + ctrY
  136.         
  137.             For lCol = -xOffset To maxSize - xOffset - 1&
  138.             
  139.                 ' validate rotated point is within bounds of the image/portion
  140.                 If targetY >= SrcY Then                    ' is Y within area of source?
  141.                     If targetY < maxY Then
  142.                         If targetX >= SrcX Then            ' is X within area of source?
  143.                             If targetX < maxX Then
  144.                                 ' validation complete, copy pixel to destination
  145.                                 CopyMemory dBytes((xOffset + lCol) * 4&, yOffset + lRow), sBytes(Int(targetX) * 4&, Int(targetY)), 4&
  146.                             End If
  147.                         End If
  148.                     End If
  149.                 End If
  150.         
  151.                 ' Excellent optimization I found (wish I remember where so I can give credit)
  152.                 ' But the logic is simple: once the initial X,Y coordinates for the
  153.                 ' current source row is found, the next point is always a constant value from
  154.                 ' the last point. In this case, increments of cosT & sinT.
  155.                 ' Therefore we don't need to recalculate targetX,targetY for every point
  156.                 ' since we did it once for current row. Thus we have 2 simple additions per pixel
  157.                 ' vs 4 multiplications & 12 additions per pixel
  158.                 targetX = targetX + cosTx
  159.                 targetY = targetY - sinTy
  160.             Next
  161.         Next
  162.         
  163.     Else
  164.         ' BiLinear interpolation with rotation. This can produce better quality
  165.         ' results but takes significantly (x4) longer. Recommend using this option
  166.         ' when you need a static rotated image, but when rotating via a scrollbar
  167.         ' or some other method where scrolling is expected to be repeated often,
  168.         ' then use the non-BiLinear method. COMPILED IS MUCH FASTER !!!
  169.         
  170.         ' Up to 4 source pixels (16 bytes) are blended for each destination pixel (4 bytes)
  171.         srcWidth = maxX - 1& ' reuse variable & subtract now vs subtraction for every pixel in the image
  172.         For lRow = -yOffset To maxSize - yOffset - 1&
  173.             
  174.             ' Calculate the rotated point in relation to host image
  175.             ' These calcs only needs to be done once per image scan line
  176.             targetX = (-xOffset - ctrX) * cosTx + (lRow - ctrY) * sinTx + ctrX
  177.             targetY = (lRow - ctrY) * cosTy - (-xOffset - ctrX) * sinTy + ctrY
  178.         
  179.             For lCol = -xOffset To maxSize - xOffset - 1&
  180.                 If targetY >= SrcY Then                    ' is Y within area of source?
  181.                     If targetY < maxY Then
  182.                         If targetX >= SrcX Then            ' is X within area of source?
  183.                             If targetX < maxX Then
  184.                             
  185.                                 tgtY = Int(targetY)     ' whole number of the double
  186.                                 If tgtY = 0& Then       ' for top down images check for last row vs 0
  187.                                     ' last row of source image, will use only this row
  188.                                     edgeOffsetY = 0&
  189.                                     fY = 0#
  190.                                 Else
  191.                                     ' will use this row & next row for blending
  192.                                     edgeOffsetY = 1&
  193.                                     fY = 1# - (targetY - tgtY) ' for top down images, use: fY = targetY-tgtY
  194.                                     ' ^ Y coordinate fraction; pct of next vertical pixel that is used
  195.                                 End If
  196.                                                
  197.                                 R = 0#: G = 0#: B = 0#: A = 0#
  198.                                 
  199.                                 tgtX = Int(targetX)  ' coordinate rounded down to whole number
  200.                                 If tgtX = srcWidth Then
  201.                                     ' at far edge of source image, will use only this pixel for blending
  202.                                     edgeOffsetX = 0&
  203.                                     fY = 0#: fX = 0#
  204.                                 Else
  205.                                     ' will use this pixel and next pixel for blending
  206.                                     edgeOffsetX = 1&
  207.                                     fX = targetX - tgtX
  208.                                     ' ^ X coordinate fraction, pct of next horizontal pixel that is used
  209.                                 End If
  210.                                 
  211.                                 For iY = 0& To edgeOffsetY
  212.                                     scalerY = Abs(iY - fY)   ' percentage of current row's pixel to blend
  213.                                     
  214.                                     If Not scalerY = 1& Then  ' else zero
  215.                                         srcRow = tgtY - iY    ' for top down images Add iY vs subtracting
  216.                                         For iX = 0& To edgeOffsetX
  217.                                             scalerX = Abs(fX - iX)   ' percentage of current column's pixel to blend
  218.                                             
  219.                                             If Not scalerX = 1& Then  ' else zero
  220.                                                 scalerX = (1# - scalerX) * (1# - scalerY) ' combine percentages
  221.                                                 srcPixel = (tgtX + iX) * 4&
  222.                                                 B = B + sBytes(srcPixel, srcRow) * scalerX
  223.                                                 G = G + sBytes(srcPixel + 1&, srcRow) * scalerX
  224.                                                 R = R + sBytes(srcPixel + 2&, srcRow) * scalerX
  225.                                                 A = A + sBytes(srcPixel + 3&, srcRow) * scalerX
  226.                                             End If
  227.                                             
  228.                                         Next
  229.                                     End If
  230.                     
  231.                                 Next
  232.                                 dstPixel = (xOffset + lCol) * 4&
  233.                                 iY = lRow + yOffset
  234.                                 dBytes(dstPixel, iY) = Int(B)
  235.                                 dBytes(dstPixel + 1&, iY) = Int(G)
  236.                                 dBytes(dstPixel + 2&, iY) = Int(R)
  237.                                 dBytes(dstPixel + 3&, iY) = Int(A)
  238.                             End If
  239.                         End If
  240.                     End If
  241.                 End If
  242.             targetX = targetX + cosTx
  243.             targetY = targetY - sinTy
  244.             Next
  245.         Next
  246.     End If
  247.     
  248.     CopyMemory ByVal VarPtrArray(dBytes), 0&, 4&
  249.     If sSA.pvData = 0& Then ' image was also mirrored
  250.         Erase sBytes()
  251.     Else                    ' remove overlay of non-mirrored image
  252.         CopyMemory ByVal VarPtrArray(sBytes), 0&, 4&
  253.     End If
  254.     
  255.     xOffset = (TopX + destWidth  2) - (maxSize  2)
  256.     yOffset = (TopY + destHeight  2) - (maxSize  2)
  257.     rHost.gdiToken = m_GDItoken
  258.     ' now render it.  We won't pass some parameters because they have been handled here or don't apply with this temp DIB
  259.     RotateImage = rHost.Render(hDC, xOffset, yOffset, , , , , , , Opacity, , (destHostDIB Is Nothing), destHostDIB, grayScale)
  260.     
  261. eh:
  262.     If Err Then
  263.         Stop
  264.         Err.Clear ' troubleshooting only, should be removed before compiling to final app
  265.         Resume
  266.     End If
  267. End Function
  268. Public Function MakeImageInverse() As Boolean
  269.     
  270.     ' Function will invert the RGB values creating a color negative of the image
  271.     ' Calling this function again, returns the image to its previous state
  272.     
  273.     If m_Handle = 0& Then Exit Function
  274.     
  275.     Dim tSA As SafeArray, gBytes() As Byte
  276.     Dim pAlpha As Byte
  277.     Dim X As Long, Y As Long
  278.     
  279.     With tSA
  280.         .cbElements = 1
  281.         .cDims = 2
  282.         .pvData = m_Pointer
  283.         .rgSABound(0).cElements = m_Height
  284.         .rgSABound(1).cElements = m_Width * 4&
  285.     End With
  286.     CopyMemory ByVal VarPtrArray(gBytes), VarPtr(tSA), 4&
  287.     
  288.     On Error Resume Next
  289.     For Y = 0& To m_Height - 1&
  290.         For X = 0& To m_Width * 4& - 1& Step 4&
  291.             pAlpha = gBytes(X + 3&, Y)
  292.             If Not pAlpha = 0 Then   ' otherwise fully transparent pixel
  293.                 gBytes(X, Y) = -gBytes(X, Y) + pAlpha
  294.                 gBytes(X + 1&, Y) = -gBytes(X + 1&, Y) + pAlpha
  295.                 gBytes(X + 2&, Y) = -gBytes(X + 2&, Y) + pAlpha
  296.             End If
  297.         Next
  298.     Next
  299.     CopyMemory ByVal VarPtrArray(gBytes), 0&, 4&
  300.     
  301.     MakeImageInverse = True
  302.     
  303. End Function
  304. Public Function MakeTransparent(ByVal TransparentColor As Long, Optional ByVal Revert As Boolean = False) As Boolean
  305.     ' if Revert = False
  306.     '   Function will convert all pixels that are of the TransparentColor to fully transparent.
  307.     '   Additionally, only if the alpha value of the color is fully opaque will the pixel become transparent.
  308.     ' if Revert = True
  309.     '   All fully transparent colors are made fully opaque and changed to the TransparentColor
  310.     
  311.     If m_Handle = 0& Then Exit Function
  312.     
  313.     Dim tSA As SafeArray, dPixels() As Long, bPixels() As Byte
  314.     Dim X As Long, Y As Long, bAlpha As Boolean
  315.     
  316.     ' convert passed color from RGB to BGRA
  317.     TransparentColor = ((TransparentColor And &HFF) * &H10000) Or ((TransparentColor  &H100) And &HFF) * &H100 _
  318.                     Or ((TransparentColor  &H10000) And &HFF) Or &HFF000000
  319.     
  320.     With tSA
  321.         .cbElements = 4
  322.         .cDims = 2
  323.         .pvData = m_Pointer
  324.         .rgSABound(0).cElements = m_Height
  325.         .rgSABound(1).cElements = m_Width
  326.     End With
  327.     CopyMemory ByVal VarPtrArray(dPixels), VarPtr(tSA), 4&
  328.     
  329.     If Revert Then
  330.         ' change all fully transparent pixels to passed color
  331.         For Y = 0& To m_Height - 1&
  332.             For X = 0& To m_Width - 1&
  333.                 If dPixels(X, Y) = 0& Then
  334.                     dPixels(X, Y) = TransparentColor
  335.                 End If
  336.             Next
  337.         Next
  338.         CopyMemory ByVal VarPtrArray(dPixels), 0&, 4&
  339.         ' image may or may not contain alpha any longer, validate it
  340.         ' The validation routine expects a 2D byte array, not long, so...
  341.         tSA.cbElements = 1
  342.         tSA.rgSABound(1).cElements = m_Width * 4&
  343.         CopyMemory ByVal VarPtrArray(bPixels), VarPtr(tSA), 4&
  344.         iparseValidateAlphaChannel bPixels(), False, bAlpha, 0&
  345.         CopyMemory ByVal VarPtrArray(bPixels), 0&, 4&
  346.         Me.Alpha = bAlpha
  347.         If bAlpha = False Then
  348.             If m_Format = imgBmpPARGB Or m_Format = imgBmpARGB Then m_Format = imgBitmap
  349.         End If
  350.         
  351.     Else
  352.         ' change all fully opaque colors matching TransparentColor to fully transparent
  353.         For Y = 0& To m_Height - 1&
  354.             For X = 0& To m_Width - 1&
  355.                 If dPixels(X, Y) = TransparentColor Then
  356.                     dPixels(X, Y) = 0&
  357.                     bAlpha = True
  358.                 End If
  359.             Next
  360.         Next
  361.         CopyMemory ByVal VarPtrArray(dPixels), 0&, 4&
  362.         If bAlpha Then
  363.             m_AlphaImage = True
  364.             If m_Format = imgBitmap Then m_Format = imgBmpPARGB
  365.         End If
  366.     End If
  367.     MakeTransparent = True
  368. End Function
  369. Public Function MirrorImage(ByVal MirrorAxisX As Boolean, ByVal MirrorAxisY As Boolean) As Boolean
  370.     
  371.     ' Function will mirror an image onto the same DIB.
  372.     ' This function should be called when any image is mirrored vs mirroring the image
  373.     ' within the Render function or rotate functions. Faster rendering will then occur.
  374.     ' Mirroring never destroys original data and can be easily unmirrored.
  375.     
  376.     ' MirrorAxisX: If true, then image is mirrored horizontally
  377.     ' MirrorAxisY: If true, then image is mirrored vertically
  378.     
  379.     If Not m_Handle = 0& Then
  380.         If MirrorAxisX Or MirrorAxisY Then
  381.             Dim tBytes() As Byte, cX As Long, cY As Long
  382.             If MirrorAxisX = True Then cX = -m_Width Else cX = m_Width
  383.             If MirrorAxisY = True Then cY = -m_Height Else cY = m_Height
  384.             MirrorDIB 0&, 0&, m_Width, m_Height, cX, cY, tBytes()
  385.             CopyMemory ByVal m_Pointer, tBytes(0, 0), m_Width * m_Height * 4&
  386.             MirrorImage = True
  387.         End If
  388.     End If
  389. End Function
  390. Friend Sub SetOriginalFormat(inStream() As Byte)
  391.     ' Purpose: Pass the original image file/bytes to this DIB from another DIB
  392.     ' This is only called by the CopyImageTo function. Note it is Friend vs Public
  393.     
  394.     m_ImageByteCache() = inStream()
  395. End Sub
  396. Private Function LoadPictureEx(ByVal FileHandle As Long, FileName As String, aStream() As Byte, _
  397.                             cX As Long, cY As Long, _
  398.                             streamOffset As Long, streamLength As Long, _
  399.                             SaveFormat As Boolean, bitDepth As Long) As Boolean
  400.     
  401.     ' PURPOSE: Marshal passed file/array to image classes for conversion to 32bpp image
  402.     ' For parameter information, see LoadPicture_File & LoadPicture_Stream
  403.     
  404.     Me.DestroyDIB
  405.     
  406.     ' various image parsers, in order of precedence
  407.     ' All 4 recognize transparency
  408.     Dim cPNG As cPNGparser  ' very fast to abort if not a PNG file
  409.     Dim cGIF As cGIFparser  ' very fast to abort if not a GIF file
  410.     Dim cICO As cICOparser  ' must parse key parts of a file. handles icons & Vista PNG Icons
  411.     Dim cBMP As cBMPparser  ' catchall. Handles bitmaps, wmf, emf & jpgs
  412.     Dim cGDI As cGDIPlus
  413.     
  414.     Dim bReturn As Boolean  ' function return value
  415.     Dim rtnRead As Long
  416.     
  417.     ' validate passed desired icon sizes
  418.     If cX < 0& Then cX = 0&
  419.     If cY < 0& Then cY = 0&
  420.     If bitDepth < 0& Then
  421.         bitDepth = 32
  422.     ElseIf bitDepth > 32 Then
  423.         bitDepth = 32
  424.     End If
  425.     
  426.     Set cPNG = New cPNGparser   ' see if image is a PNG; aborts quickly if not
  427.     If FileHandle = 0 Then
  428.         bReturn = cPNG.LoadStream(aStream(), Me, streamOffset, streamLength, m_GDItoken)
  429.     Else     ' note: processing from file is slightly faster than via array
  430.         bReturn = cPNG.LoadFile(FileHandle, FileName, Me, m_GDItoken)
  431.         'If bReturn = True Then Close #fileNum         ' close the file
  432.     End If
  433.     If Err Then MsgBox Err.Description
  434.     Set cPNG = Nothing
  435.     If Not bReturn Then
  436.         If Not FileHandle = 0& Then
  437.             streamOffset = 0&
  438.             streamLength = GetFileSize(FileHandle, 0&)
  439.             'streamLength = LOF(fileNum) ' cache length of file
  440.             ReDim aStream(streamOffset To streamLength - 1&)
  441.             'Get #fileNum, , aStream()   ' populate our stream with the file contents
  442.             'Close #fileNum
  443.             SetFilePointer FileHandle, 0&, 0&, 0&
  444.             ReadFile FileHandle, aStream(streamOffset), streamLength, rtnRead, ByVal 0&
  445.         End If
  446.         Set cGIF = New cGIFparser ' what about a GIF; aborts quickly if not
  447.         bReturn = cGIF.LoadStream(aStream(), Me, streamOffset, streamLength)
  448.         Set cGIF = Nothing
  449.         If Not bReturn Then
  450.             Set cICO = New cICOparser   ' will process Vista PNG icon if needed
  451.             bReturn = cICO.LoadStream(aStream(), cX, cY, Me, streamOffset, streamLength, bitDepth, m_GDItoken)
  452.             Set cICO = Nothing
  453.             If Not bReturn Then ' check for bmp, emf, wmf & jpg << last chance
  454.                 Set cBMP = New cBMPparser
  455.                 bReturn = cBMP.LoadStream(aStream(), Me, streamOffset, streamLength)
  456.                 Set cBMP = Nothing
  457.             End If
  458.         End If
  459.     End If
  460.     If m_Handle = 0& Then ' hmmm, not an image file processed here, we can try
  461.                           ' one more thing... Send it GDI+ if possible
  462.         If Me.isGDIplusEnabled Then
  463.             Set cGDI = New cGDIPlus
  464.             If cGDI.GDIplusLoadPNG(FileName, aStream(), Me, m_GDItoken) = True Then
  465.                 ' it worked, whatever file type it was. Convert it to PNG
  466.                 If SaveFormat = True Then
  467.                     Call cGDI.SaveToPNG(vbNullString, aStream, Me, m_GDItoken)
  468.                     FileHandle = 0& ' prevent next IF from trying to re-load it from file if applicable
  469.                 End If
  470.             End If
  471.         End If
  472.     End If
  473.     If Not m_Handle = 0 Then
  474.         If SaveFormat = True Then ' we will cache the original bytes
  475.             If iparseIsArrayEmpty(VarPtrArray(aStream)) = 0& And Not FileHandle = 0& Then
  476.                 ' we loaded the image from the file and not a stream (PNG), need to get stream
  477.                 'fileNum = FreeFile()
  478.                 'Open FileName For Binary Access Read As #fileNum
  479.                 If streamLength = 0& Then streamLength = GetFileSize(FileHandle, 0&)
  480.                 ReDim m_ImageByteCache(0 To streamLength - 1)
  481.                 SetFilePointer FileHandle, 0&, 0&, 0&
  482.                 ReadFile FileHandle, m_ImageByteCache(0), streamLength, rtnRead, ByVal 0&
  483.                 'Get #fileNum, 1, m_ImageByteCache
  484.                 'Close #fileNum
  485.             Else
  486.                 m_ImageByteCache() = aStream()
  487.             End If
  488.         End If
  489.         LoadPictureEx = True
  490.     End If
  491. End Function
  492. Private Function pvResize(ByVal destDC As Long, _
  493.                         rSizedBytes() As Byte, rMirror() As Byte, _
  494.                         Optional tHost As c32bppDIB, _
  495.                         Optional ByVal SrcX As Long, Optional ByVal scrY As Long, _
  496.                         Optional ByVal srcWidth As Long, Optional ByVal srcHeight As Long, _
  497.                         Optional ByVal destX As Long, Optional ByVal destY As Long, _
  498.                         Optional ByRef destWidth As Long, Optional ByRef destHeight As Long, _
  499.                         Optional ByRef LightAdjustment As Single = 0!) As Boolean
  500.                             
  501.     ' Function resizes an alpha image, maintaining premultiplied pixels & alpha values
  502.     ' Code originally by Carles P.V. but significantly modified for this project.
  503.     
  504.     ' Parameters:
  505.     ' destDC :: DC being rendered to, may be null
  506.     ' rSizedbytes() : array to hold resized alpha section; not used if tHost is not Nothing
  507.     ' tHost : when resizing to another DIB class, the destination DIB class
  508.     ' srcX,Y : the coordinates of the source image to start resizing from
  509.     ' srcWidth,srcHeight : the width/height of the source image to resize from
  510.     ' destX,Y : the coordinates of the destination image to resize to
  511.     ' destWidth,destHeight : the width/height of the destination image to resize to
  512.     If srcWidth = 0& Then srcWidth = m_Width
  513.     If srcHeight = 0& Then srcHeight = m_Height
  514.     Dim aNewBits() As Byte, dSA As SafeArray   ' new size, overlay of DIB pointer
  515.     Dim aOldBits() As Byte, tSA As SafeArray   ' old size, overlay of DIB pointer
  516.     
  517.     Dim xLUdbl() As Double                     ' look up table (LUT)
  518.     Dim xRatio As Double, yRatio As Double     ' scaled ratios
  519.     Dim srcPixel As Long, dstPixel As Long     ' source/destination pixel locations
  520.     Dim lCol As Long, lRow As Long             ' loop variables
  521.     
  522.     Dim newWidth As Long, newHeight As Long
  523.     
  524.     ' following used with BiLinear scaling
  525.     Dim fX As Double, fY As Double
  526.     Dim tgtX As Long, tgtY As Long
  527.     Dim edgeOffsetX As Long, edgeOffsetY As Long
  528.     Dim iX As Long, iY As Long
  529.     Dim R As Double, G As Double, B As Double, A As Double
  530.     Dim scalerX As Double, scalerY As Double
  531.     
  532.     ' fill in opitonal parameters
  533.     If Not tHost Is Nothing Then
  534.         newWidth = tHost.Width
  535.         newHeight = tHost.Height
  536.         ' Scaling ratio (ratio of actual image to scaled image)
  537.         xRatio = srcWidth / newWidth
  538.         yRatio = srcHeight / newHeight
  539.         If newWidth > tHost.Width Then newWidth = tHost.Width
  540.         If newHeight > tHost.Height Then newHeight = tHost.Height
  541.     Else
  542.         newWidth = Abs(destWidth)
  543.         newHeight = Abs(destHeight)
  544.         
  545.         ' Scaling ratio (ratio of actual image to scaled image)
  546.         xRatio = srcWidth / newWidth
  547.         yRatio = srcHeight / newHeight
  548.         ' safety checks, recalculation of bounding destination size
  549.         ' if not done, we could very easily access unallocated memory.
  550.         If destX < 0 Then   ' negative DC offset
  551.             newWidth = newWidth + destX    ' reduce width to process
  552.             destX = -destX                 ' used to offset LUT; adjust so not processing bytes not used
  553.         Else
  554.             destX = 0&                        ' fits within destination bitmap; no offsetting needed
  555.         End If
  556.         
  557.         ' now to check the vertical
  558.         If destY < 0& Then  ' negative DC offset
  559.             newHeight = newHeight + destY
  560.             destY = 0&
  561.         Else
  562.             destY = 0&
  563.         End If
  564.         
  565.     End If
  566.     If newHeight < 1& Or newWidth < 1& Then Exit Function
  567.     
  568.     With dSA    ' overlay destination array onto the passed Byte() array
  569.         .cbElements = 1
  570.         .cDims = 2
  571.         .rgSABound(0).cElements = newHeight
  572.         .rgSABound(1).cElements = newWidth * 4&
  573.         If tHost Is Nothing Then
  574.             ReDim rSizedBytes(0& To .rgSABound(1).cElements - 1&, 0& To newHeight - 1&)
  575.             .pvData = VarPtr(rSizedBytes(0&, 0&))
  576.         Else
  577.             .pvData = tHost.BitsPointer ' called by CopyImageTo & Resize routines
  578.         End If
  579.     End With
  580.     
  581.     With tSA    ' overlay source array onto our DIB
  582.         .cbElements = 1
  583.         .cDims = 2
  584.         If iparseIsArrayEmpty(VarPtrArray(rMirror)) = 0& Then
  585.             .pvData = m_Pointer                     ' source is our DIB
  586.             .rgSABound(0).cElements = m_Height
  587.             .rgSABound(1).cElements = m_Width * 4&
  588.         Else
  589.             .pvData = VarPtr(rMirror(0&, 0&))           ' source is the mirrored DIB; clipped as needed
  590.             .rgSABound(0).cElements = UBound(rMirror, 2) + 1&
  591.             .rgSABound(1).cElements = UBound(rMirror, 1) + 1&
  592.         End If
  593.     End With
  594.     
  595.     CopyMemory ByVal VarPtrArray(aNewBits), VarPtr(dSA), 4&
  596.     CopyMemory ByVal VarPtrArray(aOldBits), VarPtr(tSA), 4&
  597.     
  598.     On Error GoTo eh
  599.     
  600.     scrY = UBound(aOldBits, 2) - srcHeight - scrY + 1&     ' adjust Y position in source for bottom up DIBs
  601.     If (m_StretchQuality = False) Then
  602.         
  603.         ' Scaling LUT, cache actual X position of DIB in relation to scaled X
  604.         ' Cache one scan line of X coords so we don't have to calculate for every pixel
  605.         ReDim xLUdbl(0 To newWidth - 1&)
  606.         For lCol = 0 To newWidth - 1&
  607.             ' offset destX used for negative coordinates, X is location in source to start blending at
  608.             xLUdbl(lCol) = Int(((lCol + destX) * xRatio) + SrcX) * 4&
  609.         Next
  610.         
  611.         ' nearest neighbor algorithm
  612.         For lRow = newHeight - 1& To 0& Step -1&
  613.             '^ current scanline for the scaled image
  614.             ' offset destY is used for negative coordinates
  615.             srcPixel = Int((lRow + destY) * yRatio) + scrY  ' recalcualted once per scanline
  616.             ' current scanline for the scaled image
  617.             dstPixel = 0&
  618.             For lCol = 0& To newWidth - 1&
  619.                 ' copy into resized array the nearest raw/actual pixel
  620.                 CopyMemory aNewBits(dstPixel, lRow), aOldBits(Int(xLUdbl(lCol)), srcPixel), 4&
  621.                 dstPixel = dstPixel + 4&
  622.             Next lCol
  623.         Next lRow
  624.         
  625.     Else
  626.         
  627.         ' BiLinear interoplation, up to 4 source pixels (16 bytes) are blended for each destination pixel (4 bytes)
  628.  
  629.         ReDim xLUd(0 To newWidth - 1&)   ' work with doubles, we need the decimal portions
  630.         ' Cache one scan line of X coords so we don't have to calculate for every pixel
  631.         For lCol = 0& To newWidth - 1&
  632.             ' offset destX used for negative coordinates, X is location in source to start blending at
  633.             xLUd(lCol) = (((lCol + destX) * xRatio) + SrcX)
  634.         Next
  635.         srcWidth = srcWidth - 1& ' subtract now vs subtracting in loop below
  636.         For lRow = newHeight - 1& To 0& Step -1&
  637.             
  638.             fY = (lRow + destY) * yRatio + scrY ' get the scaled source row
  639.             tgtY = Int(fY)              ' get whole number of double
  640.             If tgtY = 0& Then         ' for top down images, test for last row vs 0
  641.                 ' last row of source image, will use only this row
  642.                 edgeOffsetY = 0&
  643.                 fY = 0#
  644.             Else
  645.                 ' will use this row & next row for blending
  646.                 edgeOffsetY = 1&
  647.                 fY = Abs(1# - (fY - tgtY)) ' for top down images, use fY = fY-tgtY
  648.                 ' ^ Y coordinate fraction; pct of next vertical pixel that is used
  649.             End If
  650.             
  651.             For lCol = 0& To newWidth - 1&
  652.                 
  653.                 R = 0#: G = 0#: B = 0#: A = 0#
  654.                 
  655.                 tgtX = Int(xLUd(lCol))  ' coordinate rounded down to whole number
  656.                 If tgtX = srcWidth Then
  657.                     ' at far edge of source image, will use only this pixel for blending
  658.                     edgeOffsetX = 0&
  659.                     fX = 0#
  660.                 Else
  661.                     ' will use this pixel and next pixel for blending
  662.                     edgeOffsetX = 1&
  663.                     fX = xLUd(lCol) - tgtX
  664.                     ' ^ X coordinate fraction, pct of next horizontal pixel that is used
  665.                 End If
  666.                 
  667.                 For iY = 0& To edgeOffsetY
  668.     
  669.                     scalerY = Abs(iY - fY)   ' percentage of current row's pixel to blend
  670.                     If Not scalerY = 1# Then  ' else result will be zero
  671.                     
  672.                         For iX = 0& To edgeOffsetX
  673.                             scalerX = Abs(fX - iX)   ' percentage of current column's pixel to blend
  674.                             
  675.                             If Not scalerX = 1# Then  ' else result will be zero
  676.                                 scalerX = (1# - scalerX) * (1# - scalerY) ' combine percentages
  677.     
  678.                                 ' Build the blended RGB values, for top down images Add iY vs subtracting
  679.                                 srcPixel = (tgtX + iX) * 4&
  680.                                 B = B + aOldBits(srcPixel, tgtY - iY) * scalerX
  681.                                 G = G + aOldBits(srcPixel + 1&, tgtY - iY) * scalerX
  682.                                 R = R + aOldBits(srcPixel + 2&, tgtY - iY) * scalerX
  683.                                 A = A + aOldBits(srcPixel + 3&, tgtY - iY) * scalerX
  684.     
  685.                             End If
  686.                         Next
  687.                     End If
  688.                 Next
  689.                 iX = lCol * 4&
  690.                 ' update destination with adjusted pixel
  691.                 aNewBits(iX, lRow) = Int(B)
  692.                 aNewBits(iX + 1&, lRow) = Int(G)
  693.                 aNewBits(iX + 2&, lRow) = Int(R)
  694.                 aNewBits(iX + 3&, lRow) = Int(A)
  695.             Next
  696.         Next
  697.     End If
  698.     
  699.     CopyMemory ByVal VarPtrArray(aOldBits), 0&, 4&
  700.     CopyMemory ByVal VarPtrArray(aNewBits), 0&, 4&
  701.     
  702.     ' the passed destWidth,destHeight params are used when rendering; we are just sizing now
  703.     destWidth = newWidth ' the parameter is ByRef, update it now
  704.     destHeight = newHeight ' the parameter is ByRef, update it now
  705.     Erase rMirror()
  706.     
  707.     If Not LightAdjustment = 0! Then
  708.         Call LightenDarken(Nothing, LightAdjustment, rSizedBytes())
  709.         LightAdjustment = 0!    ' reset, taken care of now
  710.     End If
  711.     
  712.     pvResize = True
  713. eh:
  714.     If Err Then
  715.         Err.Clear   ' troubleshooting only, should be removed before compiling to final app
  716.         Stop
  717.         Resume
  718.     End If
  719. End Function
  720. Private Function Win9xBlend(ByVal destinationDC As Long, aResizedBytes() As Byte, _
  721.                             ByVal SrcX As Long, ByVal SrcY As Long, _
  722.                             ByVal destX As Long, ByVal destY As Long, _
  723.                             ByVal destWidth As Long, ByVal destHeight As Long, _
  724.                             ByVal GlobalAlpha As Long, tHost As c32bppDIB, _
  725.                             ByVal grayScale As eGrayScaleFormulas, _
  726.                             ByVal lightAdj As Single) As Boolean
  727.     ' Function manually blends an alpha bitmap to a target DC
  728.     
  729.     ' Never called when GDI+ is available unless user forced isGDIplusEnabled=False.
  730.     ' Used when AlphaBlend is not available or when AlphaBlend is available but cannot
  731.     '   perform the graphic manipulation required
  732.     
  733.     ' Parameters identify the destination more than anything else. The source was already pre-processed if needed
  734.     
  735.     ' destinationDC :: DC to blend to
  736.     ' aResizedBytes() :: array of bytes sized to target destination blend area.
  737.     '   if array is null, then the destination size is same size as our DIB's image
  738.     ' srcX,Y :: the position on source to begin blending
  739.     ' destX,Y :: the position on destination where blending starts
  740.     ' destWidth,Height :: the amount of columns/rows to blend
  741.     ' globalAlpha :: the AlphaBlend global alpha value: between 0 and 255
  742.     ' tHost :: blending will occur DIB to DIB vs DIB to DC
  743.     ' grayScale :: if simultaneously grayscaling then the grayscale formula
  744.     ' ligthAdj :: light adjustment value adds/subtracts -100 to 100 percent intensity per pixel
  745.     
  746.     ' Special note. Having problems rendering 32bpp DIBs to WinME; artifacts are being rendered
  747.     ' from the alpha channel. Therefore, to completely eliminate this problem (hopefully), the
  748.     ' destination array will be 24bpp vs 32bpp which will then be updated and rendered onto the
  749.     ' destination DC.  32bpp would be easier, but oh well.
  750.     Dim srcBytes() As Byte, srcSA As SafeArray
  751.     Dim dstBytes() As Byte, dstSA As SafeArray
  752.     Dim srcCol As Long, srcRow As Long
  753.     Dim srcAlpha As Long, dstAlpha As Long
  754.     Dim Y As Long, X As Long
  755.     Dim sX As Long, sY As Long
  756.     Dim dX As Long, dY As Long
  757.     Dim dDC As Long, tDC As Long, hOldBmp As Long, hDib As Long
  758.     Dim Rg As Single, Gg As Single, Bg As Single
  759.     Dim gScaleByte As Long
  760.     
  761.     Dim BMPI As BITMAPINFO
  762.     
  763.     ' The following is just a wee bit confusing.
  764.     ' Our source can be 2 different objects:
  765.     '   1) Our host DIB
  766.     '   2) The passed aResizedBytes() array if pre-processing was required
  767.     ' Likewise, the destination can be 2 different objects
  768.     '   1) A passed DC handle (DIB to 24bpp bitmap - 32bpp to 24bpp)
  769.     '   2) Another DIB class if tHost is passed (32bpp to 32bpp)
  770.     ' So to use a common base for all possibilities, we use SafeArrays
  771.     
  772.     If iparseIsArrayEmpty(VarPtrArray(aResizedBytes)) = 0& Then
  773.         If lightAdj = 0! Then
  774.             srcSA.pvData = m_Pointer
  775.             ' need to tweak for negative offsets
  776.             If destX < 0& Then
  777.                 SrcX = SrcX - destX ' less area that needs to be rendered
  778.                 destWidth = destWidth + destX ' adjust amount of destination we copy
  779.                 destX = 0&                    ' set destination offset to zero
  780.             End If
  781.             If destY < 0& Then
  782.                 SrcY = SrcY - destY ' less area that needs to be rendered
  783.                 destHeight = destHeight + destY ' adjust amount of destination we copy
  784.                 destY = 0&                      ' set destination offset to zero
  785.             End If
  786.             SrcY = m_Height - SrcY  ' set DIB offset for 1st row to be blended
  787.         Else
  788.             ' if light adjustments, preprocess bytes
  789.             LightenDarken Me, lightAdj, aResizedBytes()
  790.             srcSA.pvData = VarPtr(aResizedBytes(0, 0))
  791.             SrcX = 0&: SrcY = destHeight
  792.         End If
  793.      Else ' source is the resized array
  794.          srcSA.pvData = VarPtr(aResizedBytes(0, 0))
  795.          If destX < 0& Then destX = 0& ' when negative target coords are used, we set to
  796.          If destY < 0& Then destY = 0& ' zero to match the resized array (zero-based)
  797.          SrcX = 0&: SrcY = destHeight
  798.     End If
  799.      
  800.      If Not tHost Is Nothing Then
  801.         ' need to ensure we won't be copying memory to unallocated memory
  802.         If destWidth > tHost.Width Then destWidth = tHost.Width
  803.         If destHeight > tHost.Height Then destHeight = tHost.Height
  804.      End If
  805.      If destWidth < 1& Or destHeight < 1& Then Exit Function  ' nothing to Blt, passed bad params
  806.     
  807.     If tHost Is Nothing Then
  808.         ' we need the contents of the target DC, describe its bitmap
  809.         With BMPI.bmiHeader
  810.             .biSize = 40
  811.             .biHeight = destHeight
  812.             .biWidth = destWidth
  813.             .biPlanes = 1
  814.             .biBitCount = 24    ' see Special note above
  815.         End With
  816.         dDC = GetDC(0&)
  817.         tDC = CreateCompatibleDC(dDC)
  818.         If Not tDC = 0& Then
  819.             hDib = CreateDIBSection(dDC, BMPI, 0&, dstSA.pvData, 0&, 0&)
  820.         End If
  821.         ReleaseDC 0&, dDC
  822.         If tDC = 0& Or hDib = 0& Then Exit Function
  823.         hOldBmp = SelectObject(tDC, hDib)
  824.         BitBlt tDC, 0&, 0&, destWidth, destHeight, destinationDC, destX, destY, vbSrcCopy
  825.         SelectObject tDC, hOldBmp
  826.         DeleteDC tDC
  827.         
  828.         With dstSA
  829.             .cbElements = 1
  830.             .cDims = 2
  831.             .rgSABound(0).cElements = destHeight
  832.             .rgSABound(1).cElements = iparseByteAlignOnWord(24, destWidth)
  833.         End With
  834.     
  835.     Else
  836.         ' DIB class to DIB class blending
  837.         With dstSA
  838.             .cbElements = 1
  839.             .cDims = 2
  840.             .pvData = tHost.BitsPointer
  841.             .rgSABound(0).cElements = tHost.Height
  842.             .rgSABound(0).lLbound = -(tHost.Height - destY - destHeight)
  843.             .rgSABound(1).cElements = tHost.scanWidth
  844.             .rgSABound(1).lLbound = -destX * 4
  845.         End With
  846.     End If
  847.                     
  848.     CopyMemory ByVal VarPtrArray(dstBytes), VarPtr(dstSA), 4&
  849.     
  850.     With srcSA  ' overlay onto our DIB
  851.         .cbElements = 1
  852.         .cDims = 2
  853.         If .pvData = m_Pointer Then ' using DIB as source
  854.             .rgSABound(0).cElements = m_Height
  855.             .rgSABound(1).cElements = m_Width * 4&
  856.         Else                        ' using resized array as source
  857.             .rgSABound(0).cElements = UBound(aResizedBytes, 2) + 1
  858.             .rgSABound(1).cElements = UBound(aResizedBytes, 1) + 1
  859.         End If
  860.     End With
  861.     CopyMemory ByVal VarPtrArray(srcBytes), VarPtr(srcSA), 4&
  862.                     
  863.     On Error Resume Next ' expected errors? using a corrupted pre-multiplied image possibly, but will just draw wrong
  864.     
  865.     
  866.     ' this loop is broken into several different loops to enhance speed when less options are used
  867.     ' 1. separate loop when rendering DIB to DC or DIB to DIB
  868.     ' 2. separate loop when using global alpha of 255 or using less than 255
  869.     ' 3. separate loop when grayscaling and not grayscaling
  870.     
  871.     SrcX = SrcX * 4&                    ' first pixel to be procesed
  872.     For srcRow = 0& To destHeight - 1&
  873.     
  874.         ' offset our DIB row as we go
  875.         SrcY = SrcY - 1                 ' current source row being processed
  876.         sX = SrcX                       ' 1st column of source row
  877.         dY = destHeight - srcRow - 1    ' next row for destination image
  878.         dX = 0&                         ' 1st column of destination row
  879.         
  880.         If tHost Is Nothing Then        ' DIB to DC rendering (4 pixel source to 3 pixel target)
  881.             
  882.             If grayScale = gsclNone Then
  883.                 If GlobalAlpha = &HFF& Then
  884.                     ' with full opaqueness, use separate loop, less calculations
  885.                     For srcCol = 0& To destWidth - 1&
  886.                          srcAlpha = srcBytes(sX + 3&, SrcY)     ' get its alpha value
  887.                          If (srcAlpha = &HFF&) Then
  888.                              ' copy pixel to destination, adjusting for destination row/column as needed
  889.                              CopyMemory dstBytes(dX, dY), srcBytes(sX, SrcY), 3&
  890.                          ElseIf Not srcAlpha = 0& Then
  891.                              '-- Blend
  892.                             dstAlpha = &HFF& - srcAlpha ' calculate dest alpha value
  893.                             dstBytes(dX, dY) = (dstAlpha * dstBytes(dX, dY))  &HFF + srcBytes(sX, SrcY)
  894.                             dstBytes(dX + 1&, dY) = (dstAlpha * dstBytes(dX + 1&, dY))  &HFF + srcBytes(sX + 1&, SrcY)
  895.                             dstBytes(dX + 2&, dY) = (dstAlpha * dstBytes(dX + 2&, dY))  &HFF + srcBytes(sX + 2&, SrcY)
  896.                          End If
  897.                         dX = dX + 3&
  898.                         sX = sX + 4&
  899.                      Next
  900.                 Else
  901.                     ' global alpha and per-pixel blending
  902.                     For srcCol = 0& To destWidth - 1&
  903.                          srcAlpha = srcBytes(sX + 3&, SrcY)     ' get its alpha value
  904.                          If Not srcAlpha = 0& Then
  905.                             ' following formula is for already pre-multiplied bytes
  906.                             dstAlpha = &HFF& - ((srcAlpha * GlobalAlpha)  &HFF&)
  907.                             dstBytes(dX, dY) = (dstAlpha * (dstBytes(dX, dY)) + (srcBytes(sX, SrcY) * GlobalAlpha))  &HFF
  908.                             dstBytes(dX + 1&, dY) = (dstAlpha * (dstBytes(dX + 1&, dY)) + (srcBytes(sX + 1&, SrcY) * GlobalAlpha))  &HFF
  909.                             dstBytes(dX + 2&, dY) = (dstAlpha * (dstBytes(dX + 2&, dY)) + (srcBytes(sX + 2&, SrcY) * GlobalAlpha))  &HFF
  910.                          End If
  911.                          dX = dX + 3&
  912.                          sX = sX + 4&
  913.                      Next
  914.                 End If
  915.             Else    ' gray scaling
  916.                 Call iparseGrayScaleRatios(grayScale, Rg, Gg, Bg)
  917.                 If GlobalAlpha = &HFF& Then
  918.                     ' with full opaqueness, use separate loop, less calculations
  919.                     For srcCol = 0& To destWidth - 1&
  920.                          srcAlpha = srcBytes(sX + 3&, SrcY)     ' get its alpha value
  921.                          If (srcAlpha = &HFF&) Then
  922.                              ' copy pixel to destination, adjusting for destination row/column as needed
  923.                              dstBytes(dX, dY) = (srcBytes(sX, SrcY) * Bg) + (srcBytes(sX + 1&, SrcY) * Gg) + (srcBytes(sX + 2&, SrcY) * Rg)
  924.                              dstBytes(dX + 1&, dY) = dstBytes(dX, dY)
  925.                              dstBytes(dX + 2&, dY) = dstBytes(dX, dY)
  926.                          ElseIf Not srcAlpha = 0& Then
  927.                              '-- Blend
  928.                             dstAlpha = &HFF& - srcAlpha ' calculate dest alpha value
  929.                             ' calculate green,red channel grayscale value
  930.                             gScaleByte = ((srcBytes(sX, SrcY) * Bg) + (srcBytes(sX + 1&, SrcY) * Gg) + (srcBytes(sX + 2&, SrcY) * Rg)) And &HFF
  931.                             ' blend grayscale to target
  932.                             dstBytes(dX + 1&, dY) = (dstAlpha * dstBytes(dX + 1&, dY))  &HFF + gScaleByte
  933.                             dstBytes(dX + 2&, dY) = (dstAlpha * dstBytes(dX + 2&, dY))  &HFF + gScaleByte
  934.                             dstBytes(dX, dY) = (dstAlpha * dstBytes(dX, dY))  &HFF + gScaleByte
  935.                          End If
  936.                         dX = dX + 3&
  937.                         sX = sX + 4&
  938.                      Next
  939.                 Else
  940.                     ' global alpha and per-pixel blending
  941.                     For srcCol = 0& To destWidth - 1&
  942.                          srcAlpha = srcBytes(sX + 3&, SrcY)     ' get its alpha value
  943.                          If Not srcAlpha = 0& Then
  944.                             ' following formula is for already pre-multiplied bytes
  945.                             dstAlpha = &HFF& - ((srcAlpha * GlobalAlpha)  &HFF&)
  946.                             ' calculate green,red channel grayscale value
  947.                             gScaleByte = (((srcBytes(sX, SrcY) * Bg) + (srcBytes(sX + 1&, SrcY) * Gg) + (srcBytes(sX + 2&, SrcY) * Rg)) And &HFF) * GlobalAlpha
  948.                             ' calculate green,red channels
  949.                             dstBytes(dX, dY) = (dstAlpha * (dstBytes(dX, dY)) + gScaleByte)  &HFF
  950.                             dstBytes(dX + 1&, dY) = (dstAlpha * (dstBytes(dX + 1&, dY)) + gScaleByte)  &HFF
  951.                             dstBytes(dX + 2&, dY) = (dstAlpha * (dstBytes(dX + 2&, dY)) + gScaleByte)  &HFF
  952.                          End If
  953.                          dX = dX + 3&
  954.                          sX = sX + 4&
  955.                      Next
  956.                 End If
  957.             End If
  958.     
  959.         Else    ' DIB class to DIB class rendering  (4 pixel source to 4 pixel target)
  960.         
  961.             If grayScale = gsclNone Then
  962.                 If GlobalAlpha = &HFF& Then
  963.                     ' with full opaqueness, use separate loop, less calculations
  964.                     For srcCol = 0& To destWidth - 1&
  965.                          srcAlpha = srcBytes(sX + 3&, SrcY)     ' get its alpha value
  966.                          If (srcAlpha = &HFF&) Then
  967.                              ' copy pixel to destination, adjusting for destination row/column as needed
  968.                              CopyMemory dstBytes(dX, dY), srcBytes(sX, SrcY), 4&
  969.                          ElseIf Not srcAlpha = 0& Then
  970.                              '-- Blend
  971.                             dstAlpha = &HFF& - srcAlpha ' calculate dest alpha value
  972.                             dstBytes(dX, dY) = (dstAlpha * dstBytes(dX, dY))  &HFF + srcBytes(sX, SrcY)
  973.                             dstBytes(dX + 1&, dY) = (dstAlpha * dstBytes(dX + 1&, dY))  &HFF + srcBytes(sX + 1&, SrcY)
  974.                             dstBytes(dX + 2&, dY) = (dstAlpha * dstBytes(dX + 2&, dY))  &HFF + srcBytes(sX + 2&, SrcY)
  975.                             dstBytes(dX + 3&, dY) = (dstAlpha * dstBytes(dX + 3&, dY))  &HFF + srcBytes(sX + 3&, SrcY)
  976.                          End If
  977.                         dX = dX + 4&
  978.                         sX = sX + 4&
  979.                      Next
  980.                 Else
  981.                     ' global alpha and per-pixel blending
  982.                     For srcCol = 0& To destWidth - 1&
  983.                          srcAlpha = srcBytes(sX + 3&, SrcY)     ' get its alpha value
  984.                          If Not srcAlpha = 0& Then
  985.                              ' following formula is for already pre-multiplied bytes
  986.                              dstAlpha = &HFF& - ((srcAlpha * GlobalAlpha)  &HFF&)
  987.                              dstBytes(dX, dY) = (dstAlpha * (dstBytes(dX, dY)) + (srcBytes(sX, SrcY) * GlobalAlpha))  &HFF
  988.                              dstBytes(dX + 1&, dY) = (dstAlpha * (dstBytes(dX + 1&, dY)) + (srcBytes(sX + 1&, SrcY) * GlobalAlpha))  &HFF
  989.                              dstBytes(dX + 2&, dY) = (dstAlpha * (dstBytes(dX + 2&, dY)) + (srcBytes(sX + 2&, SrcY) * GlobalAlpha))  &HFF
  990.                              dstBytes(dX + 3&, dY) = (dstAlpha * (dstBytes(dX + 3&, dY)) + (srcBytes(sX + 3, SrcY) * GlobalAlpha))  &HFF
  991.                          End If
  992.                          dX = dX + 4&
  993.                          sX = sX + 4&
  994.                      Next
  995.                 End If
  996.             Else
  997.                 Call iparseGrayScaleRatios(grayScale, Rg, Gg, Bg)
  998.                 If GlobalAlpha = &HFF& Then
  999.                     ' with full opaqueness, use separate loop, less calculations
  1000.                     For srcCol = 0& To destWidth - 1&
  1001.                          srcAlpha = srcBytes(sX + 3&, SrcY)     ' get its alpha value
  1002.                          If (srcAlpha = &HFF&) Then
  1003.                              ' copy pixel to destination, adjusting for destination row/column as needed
  1004.                              dstBytes(dX, dY) = ((srcBytes(sX, SrcY) * Bg) + (srcBytes(sX + 1&, SrcY) * Gg) + (srcBytes(sX + 2&, SrcY) * Rg))
  1005.                              dstBytes(dX + 1&, dY) = dstBytes(dX, dY)
  1006.                              dstBytes(dX + 2&, dY) = dstBytes(dX, dY)
  1007.                              dstBytes(dX + 3&, dY) = &HFF
  1008.                          ElseIf Not srcAlpha = 0& Then
  1009.                              '-- Blend
  1010.                             dstAlpha = &HFF& - srcAlpha ' calculate dest alpha value
  1011.                             gScaleByte = ((srcBytes(sX, SrcY) * Bg) + (srcBytes(sX + 1&, SrcY) * Gg) + (srcBytes(sX + 2&, SrcY) * Rg) And &HFF)
  1012.                             dstBytes(dX, dY) = (dstAlpha * dstBytes(dX, dY))  &HFF + gScaleByte
  1013.                             dstBytes(dX + 1&, dY) = (dstAlpha * dstBytes(dX + 1&, dY))  &HFF + gScaleByte
  1014.                             dstBytes(dX + 2&, dY) = (dstAlpha * dstBytes(dX + 2&, dY))  &HFF + gScaleByte
  1015.                             dstBytes(dX + 3&, dY) = (dstAlpha * dstBytes(dX + 3&, dY))  &HFF + (srcBytes(sX + 3, SrcY))
  1016.                          End If
  1017.                         dX = dX + 4&
  1018.                         sX = sX + 4&
  1019.                      Next
  1020.                 Else
  1021.                     ' global alpha and per-pixel blending
  1022.                     For srcCol = 0& To destWidth - 1&
  1023.                          srcAlpha = srcBytes(sX + 3&, SrcY)     ' get its alpha value
  1024.                          If Not srcAlpha = 0& Then
  1025.                              ' following formula is for already pre-multiplied bytes
  1026.                              dstAlpha = &HFF& - ((srcAlpha * GlobalAlpha)  &HFF&)
  1027.                              gScaleByte = (((srcBytes(sX, SrcY) * Bg) + (srcBytes(sX + 1&, SrcY) * Gg) + (srcBytes(sX + 2&, SrcY) * Rg)) And &HFF) * GlobalAlpha
  1028.                              dstBytes(dX, dY) = (dstAlpha * (dstBytes(dX, dY)) + gScaleByte)  &HFF
  1029.                              dstBytes(dX + 1&, dY) = (dstAlpha * (dstBytes(dX + 1&, dY)) + gScaleByte)  &HFF
  1030.                              dstBytes(dX + 2&, dY) = (dstAlpha * (dstBytes(dX + 2&, dY)) + gScaleByte)  &HFF
  1031.                              dstBytes(dX + 3&, dY) = (dstAlpha * (dstBytes(dX + 3&, dY)) + (srcBytes(sX + 3, SrcY) * GlobalAlpha))  &HFF
  1032.                          End If
  1033.                          dX = dX + 4&
  1034.                          sX = sX + 4&
  1035.                      Next
  1036.                 End If
  1037.             End If
  1038.         End If
  1039.     Next
  1040.     ' remove overlay
  1041.     CopyMemory ByVal VarPtrArray(srcBytes), 0&, 4&
  1042.     Erase aResizedBytes()
  1043.     If Err Then Err.Clear
  1044.     
  1045.     ' transfer results
  1046.     If tHost Is Nothing Then
  1047.         SetDIBitsToDevice destinationDC, destX, destY, destWidth, destHeight, 0&, 0&, 0&, destHeight, dstBytes(0, 0), BMPI, 0&
  1048.     End If
  1049.     CopyMemory ByVal VarPtrArray(dstBytes), 0&, 4&
  1050.     If Not hDib = 0& Then DeleteObject hDib
  1051.     
  1052.     Win9xBlend = True
  1053. End Function
  1054. Private Function MirrorDIB(ByRef SrcX As Long, ByRef SrcY As Long, _
  1055.                     ByVal srcWidth As Long, ByVal srcHeight As Long, _
  1056.                     ByRef newWidth As Long, ByRef newHeight As Long, _
  1057.                     ByRef mirrorBytes() As Byte, Optional ByRef tHost As c32bppDIB, _
  1058.                     Optional ByRef LightAdjustment As Single = 0!) As Boolean
  1059.     ' through trial and error, the rule, for mirroring images, appears to be valid for
  1060.     ' Windows drawing routines are:  Flip first, then stretch and/or rotate, then clip as needed
  1061.     
  1062.     ' For manual rendering, this has the potential of really complex offsets whether or not
  1063.     ' the pixels are read left to right, right to left, top to bottom or vice versa. Not
  1064.     ' to mention rotation and/or scaling and offsets whether or not image is to be clipped
  1065.     ' because it is being rendered at negative X,Y coordinates or the image is clipped
  1066.     ' because its scaled size is too big for the destination bitmap area. So, we will
  1067.     ' not precalculate all the possible combinations of offsets in the manual rendering
  1068.     ' routines, rather we will flip first, then allow rendering routines to process as normal.
  1069.     
  1070.     ' srcX,Y :: position in source image where mirroring begins
  1071.     ' srcWidth,Height :: amount of source image that will be mirrored
  1072.     ' newWidth,Height :: size of destination mirrored image
  1073.     ' mirrorBytes() :: byte array to hold mirrored image
  1074.     ' tHost :: called by CopyImageTo & Resize when mirroring
  1075.     
  1076.     ' is image being mirrored?
  1077.     If newWidth > 0& And newHeight > 0& Then Exit Function
  1078.     
  1079.     Dim tSA As SafeArray, srcBytes() As Byte
  1080.     Dim dSA As SafeArray, dstBytes() As Byte
  1081.     Dim X As Long, Y As Long, yOffset As Long, xOffset As Long
  1082.     
  1083.     With tSA    ' overlay array on our source DIB
  1084.         .cbElements = 1
  1085.         .cDims = 2
  1086.         .pvData = m_Pointer
  1087.         .rgSABound(0).cElements = m_Height
  1088.         .rgSABound(1).cElements = m_Width * 4&
  1089.     End With
  1090.     
  1091.     With dSA    ' overlay array on our destination object
  1092.         .cbElements = 1
  1093.         .cDims = 2
  1094.         If tHost Is Nothing Then    ' destination is mirrorBytes
  1095.             ReDim mirrorBytes(0& To srcWidth * 4& - 1&, 0& To srcHeight - 1&)
  1096.             .pvData = VarPtr(mirrorBytes(0&, 0&))
  1097.         Else                        ' destination is passed DIB class
  1098.             .pvData = tHost.BitsPointer ' called by CopyImageTo & Resize routines
  1099.         End If
  1100.         .rgSABound(0).cElements = srcHeight
  1101.         .rgSABound(1).cElements = srcWidth * 4&
  1102.     End With
  1103.     
  1104.     CopyMemory ByVal VarPtrArray(srcBytes), VarPtr(tSA), 4&
  1105.     CopyMemory ByVal VarPtrArray(dstBytes), VarPtr(dSA), 4&
  1106.     
  1107.     If newHeight < 0& Then
  1108.         If newWidth > 0& Then   ' flipping vertically only, faster/easier
  1109.             xOffset = srcWidth * 4& ' number of bytes to flip at once
  1110.             Y = SrcY    ' when flipping, our Y is adjusted from the bottom
  1111.             X = SrcX * 4&   ' starting X position in source
  1112.             For yOffset = srcHeight - 1& To 0& Step -1&
  1113.                 CopyMemory dstBytes(0&, yOffset), srcBytes(X, Y), xOffset
  1114.                 Y = Y + 1&   ' move source Y to next row
  1115.             Next
  1116.         Else   ' flipping both vertically/horizontally
  1117.             Y = SrcY ' when flipping vertically, adjust from the bottom
  1118.             For yOffset = srcHeight - 1& To 0& Step -1&
  1119.                 X = (m_Width - SrcX) * 4& - 4&  ' X adjusted from right when flipping
  1120.                 For xOffset = 0 To srcWidth * 4 - 4& Step 4&
  1121.                     CopyMemory dstBytes(xOffset, yOffset), srcBytes(X, Y), 4&
  1122.                     X = X - 4&  ' move source X to next pixel
  1123.                 Next
  1124.                 Y = Y + 1&   ' move source Y to next row
  1125.             Next
  1126.         End If
  1127.     
  1128.     Else  ' flipping horizontally only, same comments as above
  1129.         Y = m_Height - SrcY - 1& ' not fipping vertically, so read from bottom up
  1130.         For yOffset = srcHeight - 1& To 0& Step -1&
  1131.             X = (m_Width - SrcX) * 4& - 4&
  1132.             For xOffset = 0 To srcWidth * 4 - 4& Step 4&
  1133.                 CopyMemory dstBytes(xOffset, yOffset), srcBytes(X, Y), 4&
  1134.                 X = X - 4&
  1135.             Next
  1136.             Y = Y - 1&
  1137.         Next
  1138.     End If
  1139.     CopyMemory ByVal VarPtrArray(srcBytes), 0&, 4&
  1140.     CopyMemory ByVal VarPtrArray(dstBytes), 0&, 4&
  1141.     
  1142.     ' any rendering routines will now use the mirrored byte array
  1143.     ' so we ensure the bounds parameters match the mirrored array bounds
  1144.     SrcX = 0&
  1145.     SrcY = 0&
  1146.     newWidth = Abs(newWidth)
  1147.     newHeight = Abs(newHeight)
  1148.     
  1149.     ' if light adjustments, preprocess bytes
  1150.     If Not LightAdjustment = 0! Then
  1151.         Call LightenDarken(Nothing, LightAdjustment, mirrorBytes())
  1152.         LightAdjustment = 0!    ' reset, taken care of now
  1153.     End If
  1154.     MirrorDIB = True
  1155.     
  1156. End Function
  1157. Private Sub LightenDarken(cImage As c32bppDIB, ByVal Lightness As Single, rtnArray() As Byte)
  1158.     ' called by Render, Win9xRender, MirrorDB, & spt_pvResize drawing routines as needed
  1159.     
  1160.     ' Routine will lighten or darken non-transparent pixels and pass results in rtnArray
  1161.     ' Parameters:
  1162.     '   cImage :: if Nothing, then rtnArray has source bytes
  1163.     '   Lightness :: values between -100 and 100 percent. -100 will produce blackness & 100 will produce whiteness
  1164.     '   rtnArray :: the array that will hold the modified bytes
  1165.     ' Note: when rtnArray is also the source array, it must contain pre-multiplied bytes
  1166.     '       and be a 2-dimensional, zero-bound arrray
  1167.     ' Checks on the array dimensions and bounds are not made here. They are guaranteed by calling routines
  1168.     
  1169.     If Not cImage Is Nothing Then
  1170.         ' ensure an image exists if passing a dib class
  1171.         If cImage.Handle = 0 Then Exit Sub
  1172.     End If
  1173.     
  1174.     Dim tSA As SafeArray, srcBytes() As Byte
  1175.     Dim dSA As SafeArray, dstBytes() As Byte
  1176.     
  1177.     Dim X As Long, Y As Long, B As Long
  1178.     Dim tVal As Long, alphaAdj As Long
  1179.     Dim srcAlpha As Byte
  1180.     
  1181.     With tSA    ' overlay array on our source
  1182.         .cbElements = 1
  1183.         .cDims = 2
  1184.         If cImage Is Nothing Then   ' source is the return array
  1185.             .pvData = VarPtr(rtnArray(0, 0))
  1186.             .rgSABound(0).cElements = UBound(rtnArray, 2) + 1
  1187.             .rgSABound(1).cElements = UBound(rtnArray, 1) + 1
  1188.         Else                        ' source is a dib class
  1189.             .pvData = cImage.BitsPointer
  1190.             .rgSABound(0).cElements = cImage.Height
  1191.             .rgSABound(1).cElements = cImage.Width * 4&
  1192.         End If
  1193.     End With
  1194.     CopyMemory ByVal VarPtrArray(srcBytes), VarPtr(tSA), 4&
  1195.     If cImage Is Nothing Then
  1196.         dSA = tSA   ' source & destination are same
  1197.     Else
  1198.         With dSA    ' overlay array on rtnArray
  1199.             ReDim rtnArray(0 To cImage.Width * 4 - 1, 0 To cImage.Height - 1)
  1200.             .cbElements = 1
  1201.             .cDims = 2
  1202.             .pvData = VarPtr(rtnArray(0, 0))
  1203.             .rgSABound(0).cElements = cImage.Height
  1204.             .rgSABound(1).cElements = cImage.Width * 4&
  1205.         End With
  1206.     End If
  1207.     CopyMemory ByVal VarPtrArray(dstBytes), VarPtr(dSA), 4&
  1208.     Lightness = 255! * (Lightness / 100!) ' calculate pct of 255 that ligthness equates to
  1209.     
  1210.     ' when pre-multiplied bytes are in effect, we can't just add Lightness because
  1211.     '   the alpha value restricts what percent of the source rgb is actually used in relation to alpha.
  1212.     '   So we must modify lightness in relation to the alpha byte too.
  1213.     ' Separate loops used to prevent having to compare if the adjusted pixel is > 255 and also < 0
  1214.     
  1215.     For Y = 0 To UBound(srcBytes, 2)    ' loop thru the rows
  1216.     ' make lighter
  1217.         If Lightness > 0! Then
  1218.             For X = 0& To UBound(srcBytes, 1) Step 4&
  1219.                 srcAlpha = srcBytes(X + 3&, Y)
  1220.                 If Not srcAlpha = 0 Then
  1221.                     ' calculate lightness in relation to alpha
  1222.                     alphaAdj = (srcAlpha * Lightness)  255
  1223.                     For B = X To X + 2&
  1224.                         tVal = srcBytes(B, Y) + alphaAdj
  1225.                         If tVal > srcAlpha Then
  1226.                             dstBytes(B, Y) = srcAlpha
  1227.                         Else
  1228.                             dstBytes(B, Y) = (tVal And &HFF)
  1229.                         End If
  1230.                     Next
  1231.                     dstBytes(B, Y) = srcAlpha   ' ensure alpha is copied too
  1232.                 End If
  1233.             Next
  1234.         Else
  1235.     ' make darker
  1236.             For X = 0& To UBound(srcBytes, 1) Step 4&
  1237.                 srcAlpha = srcBytes(X + 3&, Y)
  1238.                 If Not srcAlpha = 0 Then
  1239.                     ' calculate lightness in relation to alpha
  1240.                     alphaAdj = (srcAlpha * Lightness)  255
  1241.                     For B = X To X + 2&
  1242.                         tVal = srcBytes(B, Y) + alphaAdj
  1243.                         If tVal < 0& Then
  1244.                             dstBytes(B, Y) = 0
  1245.                         Else
  1246.                             dstBytes(B, Y) = (tVal And &HFF)
  1247.                         End If
  1248.                     Next
  1249.                     dstBytes(B, Y) = srcAlpha   ' ensure alpha is copied too
  1250.                 End If
  1251.             Next
  1252.         End If
  1253.     Next
  1254.     
  1255.     ' remove overlays
  1256.     CopyMemory ByVal VarPtrArray(srcBytes), 0&, 4&
  1257.     CopyMemory ByVal VarPtrArray(dstBytes), 0&, 4&
  1258. End Sub
  1259. Private Sub Class_Initialize()
  1260.     ' Determine operating system for compatibility of 32bpp images
  1261.     ' http://vbnet.mvps.org/code/helpers/iswinversion.htm
  1262.     ' http://msdn2.microsoft.com/en-gb/library/ms724834.aspx
  1263.     
  1264.    Dim osType As OSVERSIONINFOEX
  1265.    Const VER_PLATFORM_WIN32_WINDOWS As Long = 1
  1266.    ' Retrieve version data for OS.
  1267.    osType.dwOSVersionInfoSize = Len(osType)
  1268.    If GetVersionEx(osType) = 0 Then
  1269.       ' The OSVERSIONINFOEX structure is only supported
  1270.       ' in NT4/SP6+ and NT5.x, so we're likely running
  1271.       ' on an earlier version of Windows. Revert structure
  1272.       ' size to OSVERSIONINFO and try again.
  1273.       osType.dwOSVersionInfoSize = Len(osType) - 8
  1274.       Call GetVersionEx(osType)
  1275.    End If
  1276.    
  1277.    ' How the m_osCAP variable works and is used througout this class
  1278.    ' Value contains 1, then AlphaBlend enabled & used when needed. Not enabled on Win9x unless overridden via isAlphaBlendFriendly
  1279.    ' Value contains 2, then GDI+ enabled & used when needed (set in isGDIplusEnabled)
  1280.    ' Value contains 4, then zLib enabled & can be used to create/read PNGs (set in isZlibEnabled). Any O/S :: no longer used; tested as needed
  1281.    ' Value contains 8, then Win98+ or Win2K+: AlphaBlend capable system else it isn't
  1282.    ' Value contains 16, then a Win98 or WinME system
  1283.    ' Value contains 32, then NT4 w/less than SP6 or Win95. Otherwise system is GDI+ capable else it isn't
  1284.    ' Note: when m_osCap contains 17, then AlphaBlend has been overridden by user
  1285.    
  1286.    
  1287.     If osType.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS Then
  1288.         If osType.dwMinorVersion = 0 Then ' Win95; can't use AlphaBlend nor GDI+
  1289.             m_osCAP = 32
  1290.         Else ' flag as Alphablend disabled, but capable & is Win98/ME
  1291.             m_osCAP = 8 Or 16
  1292.         End If
  1293.     Else
  1294.         If osType.dwMajorVersion > 4 Then ' if Win2K or better
  1295.             m_osCAP = 1 Or 8    ' flag as AlphaBlend enabled (Win2K or better) and capable
  1296.         Else ' WinNT4. If SP6 or better than GDI+ capable else not. Regardless, not AlphaBlend capable
  1297.             If osType.wServicePackMajor < 6 Then m_osCAP = 32
  1298.         End If
  1299.     End If
  1300.     Me.isGDIplusEnabled = True  ' attempt to start GDI+, test system capability
  1301.     If Me.isGDIplusEnabled Then Me.HighQualityInterpolation = True
  1302. End Sub
  1303. Private Sub Class_Terminate()
  1304.     DestroyDIB ' simply clean up
  1305. End Sub
  1306. Public Function CreateDropShadow(Optional ByVal blurDepth As Long = 4, _
  1307.                         Optional ByVal Color As Long = 12632256) As c32bppDIB
  1308.     ' Purpose: This routine creates a separate DIB class to hold a shadow.
  1309.     ' The shadow is always created based of the current image contained in this class.
  1310.     ' Should you later modify the image, you should recreate the shadow.
  1311.     ' And always draw your shadow first, using same basic rendering methods you would
  1312.     ' use for the main image, offsetting the shadow's X,Y coordinates as needed.
  1313.     
  1314.     ' Tip: Adjust shadow's X,Y coordinates equal to the blur depth of the shadow for
  1315.     ' the average use. However, the shadow's X,Y coords can be adjusted as desired.
  1316.     
  1317.     ' See RenderDropShadow_JIT also. That function renders a shadow directly without
  1318.     ' creating a separate DIB class, but has very basic rendering options.
  1319. ' this routine is basically a faster (quite faster) version of vbAccelerator's shadow class
  1320. ' http://www.vbaccelerator.com/home/VB/Code/vbMedia/Image_Processing/Drop_Shadows/article.asp
  1321. ' The speed efficiency is obtained by caching the total alpha values per column that
  1322. ' will be used for blurring. If 10 columns are used to blur, we cache 10 alpha sums.
  1323. ' Then when the next source column is queried to be added to the blur calcs, we simply
  1324. ' subtract the oldest column sum from the grand total, calculate the new column sum,
  1325. ' cache it, add that sum to the grand total and move on. This approach reduces
  1326. ' (blurDepth*blurDepth-blurDepth) calculations per pixel. Using, say, a 10 pixel blur
  1327. ' depth, the savings are immense: a 69x100 image; my version: 38 ms, vbAccelerator: 232 ms
  1328.     If m_Handle = 0 Then Exit Function
  1329.     Dim X As Long, Y As Long
  1330.     Dim vTally() As Long
  1331.     Dim tAlpha As Long, tColumn As Long, tAvg As Long
  1332.     Dim dBytes() As Byte, tSA As SafeArray
  1333.     Dim t2xBlur As Long
  1334.     Dim R As Long, G As Long, B As Long
  1335.     
  1336.     Dim srcBytes() As Byte, sSA As SafeArray
  1337.     Dim shadowDIB As c32bppDIB
  1338.     
  1339.     Dim initY As Long, initYstop As Long, initYstart As Long
  1340.     Dim initX As Long, initXstop As Long
  1341.     
  1342.     If blurDepth < 0 Then
  1343.         blurDepth = 0
  1344.     ElseIf blurDepth > 10 Then
  1345.         blurDepth = 10
  1346.     End If
  1347.     t2xBlur = blurDepth * 2
  1348.     
  1349.     Set shadowDIB = New c32bppDIB
  1350.     shadowDIB.gdiToken = m_GDItoken
  1351.     shadowDIB.InitializeDIB m_Width + t2xBlur, m_Height + t2xBlur
  1352.     
  1353.     With tSA    ' overlay array on our destination object
  1354.         .cbElements = 1
  1355.         .cDims = 2
  1356.         .pvData = shadowDIB.BitsPointer
  1357.         .rgSABound(0).cElements = m_Height + t2xBlur
  1358.         .rgSABound(1).cElements = (m_Width + t2xBlur) * 4&
  1359.     End With
  1360.     CopyMemory ByVal VarPtrArray(dBytes), VarPtr(tSA), 4&
  1361.     
  1362.     With sSA    ' overlay array on our destination object
  1363.         .cbElements = 1
  1364.         .cDims = 2
  1365.         .pvData = m_Pointer ' called by CopyImageTo & Resize routines
  1366.         .rgSABound(0).cElements = m_Height
  1367.         .rgSABound(1).cElements = m_Width * 4&
  1368.     End With
  1369.     CopyMemory ByVal VarPtrArray(srcBytes), VarPtr(sSA), 4&
  1370.     
  1371.     R = Color And &HFF
  1372.     G = (Color  &H100&) And &HFF
  1373.     B = (Color  &H10000) And &HFF
  1374.     
  1375.     tAvg = (t2xBlur + 1) * (t2xBlur + 1)    ' how many pixels are being blurred
  1376.     
  1377.     ReDim vTally(0 To t2xBlur)              ' number of blur columns per pixel
  1378.     
  1379.     For Y = 0 To m_Height + t2xBlur - 1     ' loop thru shadow dib
  1380.     
  1381.         FillMemory vTally(0), (t2xBlur + 1) * 4, 0  ' reset column totals
  1382.         
  1383.         If Y < t2xBlur Then         ' y does not exist in source
  1384.             initYstart = 0          ' use 1st row
  1385.         Else
  1386.             initYstart = Y - t2xBlur ' start n blur rows above y
  1387.         End If
  1388.         ' how may source rows can we use for blurring?
  1389.         If Y < m_Height Then initYstop = Y Else initYstop = m_Height - 1
  1390.         
  1391.         tAlpha = 0  ' reset alpha sum
  1392.         tColumn = 0    ' reset column counter
  1393.         
  1394.         ' the first n columns will all be zero
  1395.         ' only the far right blur column has values; tally them
  1396.         For initY = initYstart To initYstop
  1397.             tAlpha = tAlpha + srcBytes(3, initY)
  1398.         Next
  1399.         ' assign the right column value
  1400.         vTally(t2xBlur) = tAlpha
  1401.         
  1402.         For X = 3 To (m_Width - 2) * 4 - 1 Step 4
  1403.             ' loop thru each source pixel's alpha
  1404.             
  1405.             ' set shadow alpha using blur average
  1406.             dBytes(X, Y) = tAlpha  tAvg
  1407.             ' and set shadow color
  1408.             Select Case dBytes(X, Y)
  1409.             Case 255
  1410.                 dBytes(X - 1, Y) = R
  1411.                 dBytes(X - 2, Y) = G
  1412.                 dBytes(X - 3, Y) = B
  1413.             Case 0
  1414.             Case Else
  1415.                 dBytes(X - 1, Y) = R * dBytes(X, Y)  255
  1416.                 dBytes(X - 2, Y) = G * dBytes(X, Y)  255
  1417.                 dBytes(X - 3, Y) = B * dBytes(X, Y)  255
  1418.             End Select
  1419.             ' remove the furthest left column's alpha sum
  1420.             tAlpha = tAlpha - vTally(tColumn)
  1421.             ' count the next column of alphas
  1422.             vTally(tColumn) = 0&
  1423.             For initY = initYstart To initYstop
  1424.                 vTally(tColumn) = vTally(tColumn) + srcBytes(X + 4, initY)
  1425.             Next
  1426.             ' add the new column's sum to the overall sum
  1427.             tAlpha = tAlpha + vTally(tColumn)
  1428.             ' set the next column to be recalculated
  1429.             tColumn = (tColumn + 1) Mod (t2xBlur + 1)
  1430.         Next
  1431.         
  1432.         ' now to finish blurring from right edge of source
  1433.         For X = X To (m_Width + t2xBlur - 1) * 4 - 1 Step 4
  1434.             dBytes(X, Y) = tAlpha  tAvg
  1435.             Select Case dBytes(X, Y)
  1436.             Case 255
  1437.                 dBytes(X - 1, Y) = R
  1438.                 dBytes(X - 2, Y) = G
  1439.                 dBytes(X - 3, Y) = B
  1440.             Case 0
  1441.             Case Else
  1442.                 dBytes(X - 1, Y) = R * dBytes(X, Y)  255
  1443.                 dBytes(X - 2, Y) = G * dBytes(X, Y)  255
  1444.                 dBytes(X - 3, Y) = B * dBytes(X, Y)  255
  1445.             End Select
  1446.             ' remove this column's alpha sum
  1447.             tAlpha = tAlpha - vTally(tColumn)
  1448.             ' set next column to be removed
  1449.             tColumn = (tColumn + 1) Mod (t2xBlur + 1)
  1450.         Next
  1451.     Next
  1452.     
  1453.     CopyMemory ByVal VarPtrArray(srcBytes), 0&, 4&
  1454.     CopyMemory ByVal VarPtrArray(dBytes), 0&, 4&
  1455.     
  1456.     shadowDIB.Alpha = True
  1457.     shadowDIB.ImageType = imgBmpPARGB
  1458.     shadowDIB.gdiToken = m_GDItoken
  1459.     Set CreateDropShadow = shadowDIB
  1460.     
  1461. End Function
  1462. Public Function RenderDropShadow_JIT(ByVal hDC As Long, ByVal X As Long, ByVal Y As Long, _
  1463.                                 Optional ByVal blurDepth As Long = 4, _
  1464.                                 Optional ByVal Color As Long = 12632256, _
  1465.                                 Optional ByVal Opacity As Long = 50) As Boolean
  1466.     ' Purpose: A simple version of the CreateShadow routine.
  1467.     ' Useful should the rendered image not be rotated, mirrored, or stretched and you
  1468.     ' simply want to draw a shadow on demand.
  1469.     
  1470.     ' FYI: JIT is an acronym meaning Just In Time
  1471.         
  1472.     Dim dibShadow As c32bppDIB
  1473.     Set dibShadow = CreateDropShadow(blurDepth, Color)
  1474.     If Not dibShadow Is Nothing Then
  1475.         RenderDropShadow_JIT = dibShadow.Render(hDC, X, Y, , , , , , , Opacity)
  1476.     End If
  1477.         
  1478. End Function
  1479. Public Property Let gdiToken(Token As Long)
  1480.     ' Everytime a GDI+ API function is called, the class calls GDI+ apis to
  1481.     ' create a GDI+ token first then destroys the token after the function is called.
  1482.     
  1483.     ' This occurs quite often. However, you can create your own token by calling
  1484.     ' GdiplusStartup and then passing the token to each class for the class to use.
  1485.     ' You would call GdiplusShutdown during your main form's Terminate event to
  1486.     ' release the Token.
  1487.     
  1488.     ' When Token is zero, the classes will revert to creating a token on demand.
  1489.     ' When the Token is not zero, any other DIB class created by this class will
  1490.     ' pass the token. The only routine that creates a new instance is the
  1491.     ' CreateDropShadow method. However, CopyImageTo, Resize and others may create
  1492.     ' a temporary DIB class and will also pass the token to that class as needed
  1493.     
  1494.     m_GDItoken = Token
  1495.     
  1496. End Property
  1497. Public Property Get gdiToken() As Long
  1498.     ' returns the GDI+ token if one was created
  1499.     gdiToken = m_GDItoken
  1500. End Property
  1501. Public Function GetDroppedFileNames(OLEDragDrop_DataObject As DataObject) As Boolean
  1502.     ' This function is only included to make things a little easier for those that want
  1503.     ' to fully support unicode filenames whereas VB only provides ANSI filenames.
  1504.     ' It is designed to be called from your OLEDragDrop event, passing that event's Data
  1505.     ' parameter's object like so:
  1506.     
  1507.     ' If cImage.GetDroppedFileNames(Data) = True Then
  1508.     '   [add code here]. The Data object will contain unicode filenames as necessary
  1509.     ' Else
  1510.     '   [add code here]. No file names were dropped, something else was
  1511.     ' End If
  1512.     
  1513.     ' The function will return True if any files were dropped and the passed data
  1514.     ' object will contain valid unicode filenames as necessary.
  1515.     
  1516.     ' Caution: Editing this routine after it has been called may crash the IDE
  1517.     ' I believe I have fixed that issue but am not 100% positive
  1518.     ' See posting by John Kleinen for more information regarding this method of calling GetData
  1519.     ' http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=49268&lngWId=1
  1520.     
  1521.     If OLEDragDrop_DataObject Is Nothing Then Exit Function
  1522.     If OLEDragDrop_DataObject.GetFormat(vbCFFiles) = False Then Exit Function
  1523.     
  1524.     Dim fmtEtc As FORMATETC, pMedium As STGMEDIUM
  1525.     Dim dFiles As DROPFILES
  1526.     Dim Vars(0 To 1) As Variant, pVars(0 To 1) As Long, pVartypes(0 To 1) As Integer
  1527.     Dim varRtn As Variant
  1528.     Dim iFiles As Long, iCount As Long, hDrop As Long
  1529.     Dim lLen As Long, sFiles() As String
  1530.     
  1531.     Dim IID_IDataObject As Long ' IDataObject Interface ID
  1532.     Const IDataObjVTable_GetData As Long = 12 ' 4th vtable entry
  1533.     Const CC_STDCALL As Long = 4&
  1534.     Const TYMED_HGLOBAL = 1
  1535.     Const DVASPECT_CONTENT = 1
  1536.     With fmtEtc
  1537.         .cfFormat = vbCFFiles         ' same as CF_DROP
  1538.         .lIndex = -1                    ' want all data
  1539.         .TYMED = TYMED_HGLOBAL        ' want global ptr to files
  1540.         .dwAspect = DVASPECT_CONTENT  ' no rendering
  1541.     End With
  1542.     ' The IDataObject pointer is 16 bytes after VBs DataObject
  1543.     CopyMemory IID_IDataObject, ByVal ObjPtr(OLEDragDrop_DataObject) + 16, 4&
  1544.     
  1545.     ' Here we are going to do something very new to me and kinda cool
  1546.     ' Since we know the objPtr of the IDataObject interface, we therefore know
  1547.     ' the beginning of the interface's VTable
  1548.     
  1549.     ' So, if we know the VTable address and we know which function index we want
  1550.     ' to call, we can call it directly using the following OLE API. Otherwise we
  1551.     ' would need to use a TLB to define the IDataObject interface since VB doesn't
  1552.     ' 't expose it. This has some really neat implications if you think about it.
  1553.     ' The IDataObject function we want is GetData which is the 4th function in
  1554.     ' the VTable... http://msdn2.microsoft.com/en-us/library/ms688421.aspx
  1555.     
  1556.     pVartypes(0) = vbLong: Vars(0) = VarPtr(fmtEtc): pVars(0) = VarPtr(Vars(0))
  1557.     pVartypes(1) = vbLong: Vars(1) = VarPtr(pMedium): pVars(1) = VarPtr(Vars(1))
  1558.     
  1559.     ' The variants are required by the OLE API: http://msdn2.microsoft.com/en-us/library/ms221473.aspx
  1560.     If DispCallFunc(IID_IDataObject, IDataObjVTable_GetData, CC_STDCALL, _
  1561.                         vbLong, 2, pVartypes(0), pVars(0), varRtn) = 0 Then
  1562.         
  1563.         If pMedium.Data = 0 Then
  1564.             Exit Function ' nothing to do
  1565.         Else
  1566.             ' we have a pointer to the files, kinda sorta
  1567.             CopyMemory hDrop, ByVal pMedium.Data, 4&
  1568.             If Not hDrop = 0 Then
  1569.                 ' the hDrop is a pointer to a DROPFILES structure
  1570.                 ' copy the 20-byte structure for our use
  1571.                 CopyMemory dFiles, ByVal hDrop, 20&
  1572.             End If
  1573.         End If
  1574.         
  1575.         If dFiles.fWide = 0 Then ' ansi
  1576.             GlobalFree pMedium.Data
  1577.         
  1578.         Else
  1579.             ' use the pFiles member to track offsets for file names
  1580.             dFiles.pFiles = dFiles.pFiles + hDrop
  1581.             ReDim sFiles(1 To OLEDragDrop_DataObject.Files.Count)
  1582.         
  1583.             For iCount = 1 To UBound(sFiles)
  1584.                 ' get the length of the current file & multiply by 2 because it is unicode
  1585.                 ' lstrLenW is supported in Win9x
  1586.                 lLen = lstrlenW(ByVal dFiles.pFiles) * 2
  1587.                 sFiles(iCount) = String$(lLen  2, 0)    ' build a buffer to hold the file name
  1588.                 CopyMemory ByVal StrPtr(sFiles(iCount)), ByVal dFiles.pFiles, lLen ' populate the buffer
  1589.                 ' move the pointer to location for next file, adding 2 because of a double null separator/delimiter btwn file names
  1590.                 dFiles.pFiles = dFiles.pFiles + lLen + 2
  1591.             Next
  1592.             
  1593.             GlobalFree pMedium.Data
  1594.             OLEDragDrop_DataObject.Files.Clear
  1595.             For iCount = 1 To iCount - 1
  1596.                 OLEDragDrop_DataObject.Files.Add sFiles(iCount), iCount
  1597.             Next
  1598.             
  1599.         End If
  1600.         
  1601.         GetDroppedFileNames = True
  1602.     End If
  1603.     
  1604. End Function
  1605. Public Function GetPastedFileNames(ListOfFiles() As String) As Long
  1606.     ' Another support function for unicode filenames. The filenames returned by
  1607.     ' VB's Clipboard object only contains ANSI.
  1608.     
  1609.     ' This function will return the number of file names in the clipboard, if any.
  1610.     ' The returned string array will contain the unicode/ansi filenames as needed.
  1611.     ' If the function returns zero, then the string array is not valid
  1612.     Dim hDrop As Long
  1613.     Dim sFile As String
  1614.     Dim lLen As Long
  1615.     Dim iCount As Long
  1616.     Dim dFiles As DROPFILES
  1617.    ' Get handle to CF_HDROP if any:
  1618.    If OpenClipboard(0&) = 0 Then Exit Function
  1619.         
  1620.     hDrop = GetClipboardData(vbCFFiles)
  1621.     If Not hDrop = 0 Then   ' then copied/cut files exist in memory
  1622.         iCount = DragQueryFile(hDrop, -1&, vbNullString, 0)
  1623.         ' the hDrop is a pointer to a DROPFILES structure
  1624.         ' copy the 20-byte structure for our use
  1625.         CopyMemory dFiles, ByVal hDrop, 20&
  1626.         ' use the pFiles member to track offsets for file names
  1627.         dFiles.pFiles = dFiles.pFiles + hDrop
  1628.     
  1629.         ReDim ListOfFiles(1 To iCount)
  1630.     
  1631.         For iCount = 1 To iCount
  1632.             If dFiles.fWide = 0 Then   ' ANSI text, use API to get file name
  1633.                lLen = DragQueryFile(hDrop, iCount - 1, vbNullString, 0&)       ' query length
  1634.                ListOfFiles(iCount) = String$(lLen, 0)                          ' set up buffer
  1635.                DragQueryFile hDrop, iCount - 1, ListOfFiles(iCount), lLen + 1  ' populate buffer
  1636.             Else
  1637.                ' get the length of the current file & multiply by 2 because it is unicode
  1638.                ' lstrLenW is supported in Win9x
  1639.                lLen = lstrlenW(ByVal dFiles.pFiles) * 2
  1640.                sFile = String$(lLen  2, 0)    ' build a buffer to hold the file name
  1641.                CopyMemory ByVal StrPtr(sFile), ByVal dFiles.pFiles, lLen ' populate the buffer
  1642.                ' move the pointer to location for next file, adding 2 because of a double null separator/delimiter btwn file names
  1643.                dFiles.pFiles = dFiles.pFiles + lLen + 2
  1644.                ' add our file name to the list.
  1645.                ListOfFiles(iCount) = sFile ' this may contain unicode characters if your system supports it
  1646.            End If
  1647.         Next
  1648.         
  1649.         GetPastedFileNames = iCount - 1
  1650.         
  1651.     End If
  1652.     CloseClipboard
  1653. End Function