www.pudn.com > storm > clsGradient.cls


VERSION 1.0 CLASS 
BEGIN 
  MultiUse = -1  'True 
END 
Attribute VB_Name = "clsGradient" 
Attribute VB_GlobalNameSpace = False 
Attribute VB_Creatable = True 
Attribute VB_PredeclaredId = False 
Attribute VB_Exposed = False 
'I got this module from somewhere else but it is the module which applys 
'the gradient to the pictures which are used in the status bar 
 
Option Explicit 
 
'Property Storage Variables 
Private mlColor1    As Long 
Private mlColor2    As Long 
Private mfAngle     As Single 
 
'Property Default Constants - Colors and Angle match Kath-Rock logo. 
Private Const mlDefColor1   As Long = &HFFFFD0  'Very Light Blue 
Private Const mlDefColor2   As Long = &H400000  'Very Dark Blue 
Private Const mfDefAngle    As Single = 315     'Upper-Left to Lower-Right 
 
'Misc Constants 
Private Const PI    As Double = 3.14159265358979 
Private Const RADS  As Double = PI / 180    ' * RADS = radians 
 
'TypeDefs 
Private Type PointSng   'Internal Point structure 
    X   As Single       'Uses Singles for more precision. 
    Y   As Single 
End Type 
 
Private Type PointAPI   'API Point structure 
    X   As Long 
    Y   As Long 
End Type 
 
Private Type RectAPI    'API Rect structure 
    Left    As Long 
    Top     As Long 
    Right   As Long 
    Bottom  As Long 
End Type 
 
'API functions and Constants 
Private Const PS_SOLID As Long = 0  'Solid Pen Style (Used for CreatePen()) 
Private Declare Function CreatePen Lib "gdi32" (ByVal nPenStyle As Long, ByVal nWidth As Long, ByVal crColor As Long) As Long 
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long 
Private Declare Function GetClientRect Lib "user32" (ByVal hWnd As Long, lpRect As RectAPI) As Long 
Private Declare Function GetPixel Lib "gdi32" (ByVal hDC As Long, ByVal X As Long, ByVal Y As Long) As Long 
Private Declare Function GetSysColor Lib "user32" (ByVal nIndex As Long) As Long 
Private Declare Function GetTickCount Lib "kernel32" () As Long 
Private Declare Function LineTo Lib "gdi32" (ByVal hDC As Long, ByVal X As Long, ByVal Y As Long) As Long 
Private Declare Function LockWindowUpdate Lib "user32" (ByVal hWndLock As Long) As Long 
Private Declare Function MoveToEx Lib "gdi32" (ByVal hDC As Long, ByVal X As Long, ByVal Y As Long, lpPoint As PointAPI) As Long 
Private Declare Function SelectObject Lib "gdi32" (ByVal hDC As Long, ByVal hObject As Long) As Long 
 
Public Function Draw(picObj As Object) As Boolean 
 
'Note: This class uses API functions to draw. If the 
'      destination object is in AutoRedraw mode, the 
'      Refresh method for that object must be invoked. 
 
'picObj can be a Form or PictureBox. 
 
Dim lRet    As Long 
Dim lIdx    As Long 
Dim lTime   As Long 
Dim uRect   As RectAPI 
 
'    lTime = GetTickCount() 
     
    On Error GoTo LocalError 
     
    'Stop the window from updating until we're finished. 
    lRet = LockWindowUpdate(picObj.hWnd) 
     
    'Get the client rect in pixels 
    lRet = GetClientRect(picObj.hWnd, uRect) 
     
    'Test for possible errors (GetClientRect failure or Rect < 2 pixels) 
    If lRet <> 0 Then 
        If uRect.Right > 1 And uRect.Bottom > 1 Then 
            lIdx = DrawGradient(picObj.hDC, uRect.Right, uRect.Bottom) 
            Draw = (lIdx > 0) 
        End If 
    End If 
     
    'My P3-500 took 99 millisecs (.099 secs) to create and draw 2554 diagonal 
    'lines at 315 degrees. That was frmDemo maximized on a 1280 x 1024 screen. 
    'At this speed I can redraw an entire 1280px. screen over 10 times per second. 
     
    'Same size rect at a 0 degree angle took 48 millisecs (.048 secs) to create and 
    'draw 1278 lines. This speed can redraw a 1280px. screen 20 times per second. 
     
    'Uncomment the two lines below and the lTime line at the top 
    'of this function to test the times on your PC. 
     
'    lTime = GetTickCount() - lTime 
'    MsgBox CStr(lIdx / 2) & " lines drawn in " & CStr(lTime) & " milliseconds" 
         
NormalExit: 
    'Unlock the window to allow it to update now. 
    lRet = LockWindowUpdate(0) 
    Exit Function 
     
LocalError: 
    MsgBox Err.Description, vbExclamation 
    Resume NormalExit 
 
End Function 
Public Function BlendColors(ByVal lColor1 As Long, ByVal lColor2 As Long, ByVal lSteps As Long, laRetColors() As Long) As Long 
 
'Creates an array of colors blending from 
'Color1 to Color2 in lSteps number of steps. 
'Returns the count and fills the laRetColors() array. 
 
Dim lIdx    As Long 
Dim lRed    As Long 
Dim lGrn    As Long 
Dim lBlu    As Long 
Dim fRedStp As Single 
Dim fGrnStp As Single 
Dim fBluStp As Single 
 
    'Stop possible error 
    If lSteps < 2 Then lSteps = 2 
     
    'Extract Red, Blue and Green values from the start and end colors. 
    lRed = (lColor1 And &HFF&) 
    lGrn = (lColor1 And &HFF00&) / &H100 
    lBlu = (lColor1 And &HFF0000) / &H10000 
     
    'Find the amount of change for each color element per color change. 
    fRedStp = Div(CSng((lColor2 And &HFF&) - lRed), CSng(lSteps)) 
    fGrnStp = Div(CSng(((lColor2 And &HFF00&) / &H100&) - lGrn), CSng(lSteps)) 
    fBluStp = Div(CSng(((lColor2 And &HFF0000) / &H10000) - lBlu), CSng(lSteps)) 
     
    'Create the colors 
    ReDim laRetColors(lSteps - 1) 
    laRetColors(0) = lColor1            'First Color 
    laRetColors(lSteps - 1) = lColor2   'Last Color 
    For lIdx = 1 To lSteps - 2          'All Colors between 
        laRetColors(lIdx) = CLng(lRed + (fRedStp * CSng(lIdx))) + _ 
            (CLng(lGrn + (fGrnStp * CSng(lIdx))) * &H100&) + _ 
            (CLng(lBlu + (fBluStp * CSng(lIdx))) * &H10000) 
    Next lIdx 
     
    'Return number of colors in array 
    BlendColors = lSteps 
 
End Function 
Private Function DrawGradient(ByVal hDC As Long, ByVal lWidth As Long, ByVal lHeight As Long) As Long 
 
Dim bDone       As Boolean 
Dim iIncX       As Integer 
Dim iIncY       As Integer 
Dim lIdx        As Long 
Dim lRet        As Long 
Dim hPen        As Long 
Dim hOldPen     As Long 
Dim lPointCnt   As Long 
Dim laColors()  As Long 
Dim fMovX       As Single 
Dim fMovY       As Single 
Dim fDist       As Single 
Dim fAngle      As Single 
Dim fLongSide   As Single 
Dim uTmpPt      As PointAPI 
Dim uaPts()     As PointAPI 
Dim uaTmpPts()  As PointSng 
     
    On Error GoTo LocalError 
     
    'Start with center of rect 
    ReDim uaTmpPts(2) 
    uaTmpPts(2).X = Int(lWidth / 2) 
    uaTmpPts(2).Y = Int(lHeight / 2) 
     
    'Calc distance to furthest edge as if rect were square 
    fLongSide = IIf(lWidth > lHeight, lWidth, lHeight) 
    fDist = (Sqr((fLongSide ^ 2) + (fLongSide ^ 2)) + 2) / 2 
     
    'Create points to the left and the right at a 0º angle (horizontal) 
    uaTmpPts(0).X = uaTmpPts(2).X - fDist 
    uaTmpPts(0).Y = uaTmpPts(2).Y 
    uaTmpPts(1).X = uaTmpPts(2).X + fDist 
    uaTmpPts(1).Y = uaTmpPts(2).Y 
     
    'Lines will be drawn perpendicular to mfAngle so 
    'add 90º and correct for 360º wrap 
    fAngle = CDbl(mfAngle + 90) - Int(Int(CDbl(mfAngle + 90) / 360#) * 360#) 
     
    'Rotate second and third points to fAngle 
    Call RotatePoint(uaTmpPts(2), uaTmpPts(0), fAngle) 
    Call RotatePoint(uaTmpPts(2), uaTmpPts(1), fAngle) 
     
    'We now have a line that crosses the center and 
    'two sides of the rect at the correct angle. 
     
    'Calc the starting quadrant, direction of and amount of first move 
    '(fMovX, fMovY moves line from center to starting edge) 
    'and direction of each incremental move (iIncX, iIncY). 
    Select Case mfAngle 
        Case 0 To 90 
            'Left Bottom 
            If Abs(uaTmpPts(0).X - uaTmpPts(1).X) <= Abs(uaTmpPts(0).Y - uaTmpPts(1).Y) Then 
                'Move line to left edge; Draw left to right 
                fMovX = IIf(uaTmpPts(0).X > uaTmpPts(1).X, -uaTmpPts(0).X, -uaTmpPts(1).X) 
                fMovY = 0 
                iIncX = 1 
                iIncY = 0 
            Else 
                'Move line to bottom edge; Draw bottom to top 
                fMovX = 0 
                fMovY = IIf(uaTmpPts(0).Y > uaTmpPts(1).Y, lHeight - uaTmpPts(1).Y, lHeight - uaTmpPts(0).Y) 
                iIncX = 0 
                iIncY = -1 
            End If 
        Case 90 To 180 
            'Right Bottom 
            If Abs(uaTmpPts(0).X - uaTmpPts(1).X) <= Abs(uaTmpPts(0).Y - uaTmpPts(1).Y) Then 
                'Move line to right edge; Draw right to left 
                fMovX = IIf(uaTmpPts(0).X > uaTmpPts(1).X, lWidth - uaTmpPts(1).X, lWidth - uaTmpPts(0).X) 
                fMovY = 0 
                iIncX = -1 
                iIncY = 0 
            Else 
                'Move line to bottom edge; Draw bottom to top 
                fMovX = 0 
                fMovY = IIf(uaTmpPts(0).Y > uaTmpPts(1).Y, lHeight - uaTmpPts(1).Y, lHeight - uaTmpPts(0).Y) 
                iIncX = 0 
                iIncY = -1 
            End If 
        Case 180 To 270 
            'Right Top 
            If Abs(uaTmpPts(0).X - uaTmpPts(1).X) <= Abs(uaTmpPts(0).Y - uaTmpPts(1).Y) Then 
                'Move line to right edge; Draw right to left 
                fMovX = IIf(uaTmpPts(0).X > uaTmpPts(1).X, lWidth - uaTmpPts(1).X, lWidth - uaTmpPts(0).X) 
                fMovY = 0 
                iIncX = -1 
                iIncY = 0 
            Else 
                'Move line to top edge; Draw top to bottom 
                fMovX = 0 
                fMovY = IIf(uaTmpPts(0).Y > uaTmpPts(1).Y, -uaTmpPts(0).Y, -uaTmpPts(1).Y) 
                iIncX = 0 
                iIncY = 1 
            End If 
        Case Else   '(270 to 360) 
            'Left Top 
            If Abs(uaTmpPts(0).X - uaTmpPts(1).X) <= Abs(uaTmpPts(0).Y - uaTmpPts(1).Y) Then 
                'Move line to left edge; Draw left to right 
                fMovX = IIf(uaTmpPts(0).X > uaTmpPts(1).X, -uaTmpPts(0).X, -uaTmpPts(1).X) 
                fMovY = 0 
                iIncX = 1 
                iIncY = 0 
            Else 
                'Move line to top edge; Draw top to bottom 
                fMovX = 0 
                fMovY = IIf(uaTmpPts(0).Y > uaTmpPts(1).Y, -uaTmpPts(0).Y, -uaTmpPts(1).Y) 
                iIncX = 0 
                iIncY = 1 
            End If 
    End Select 
     
    'At this point we could calculate where the lines will cross the rect edges, but 
    'this would slow things down. The picObj clipping region will take care of this. 
     
    'Start with 1000 points and add more if needed. This increases 
    'speed by not re-dimming the array in each loop. 
    ReDim uaPts(999) 
     
    'Set the first two points in the array 
    uaPts(0).X = uaTmpPts(0).X + fMovX 
    uaPts(0).Y = uaTmpPts(0).Y + fMovY 
    uaPts(1).X = uaTmpPts(1).X + fMovX 
    uaPts(1).Y = uaTmpPts(1).Y + fMovY 
     
    lIdx = 2 
    'Create the rest of the points by incrementing both points 
    'on each line iIncX, iIncY from the previous line's points. 
    'Where we stop depends on the direction of travel. 
    'We'll continue until both points in a set reach the end. 
    While Not bDone 
        uaPts(lIdx).X = uaPts(lIdx - 2).X + iIncX 
        uaPts(lIdx).Y = uaPts(lIdx - 2).Y + iIncY 
        lIdx = lIdx + 1 
        Select Case True 
            Case iIncX > 0  'Moving Left to Right 
                bDone = uaPts(lIdx - 1).X > lWidth And uaPts(lIdx - 2).X > lWidth 
            Case iIncX < 0  'Moving Right to Left 
                bDone = uaPts(lIdx - 1).X < 0 And uaPts(lIdx - 2).X < 0 
            Case iIncY > 0  'Moving Top to Bottom 
                bDone = uaPts(lIdx - 1).Y > lHeight And uaPts(lIdx - 2).Y > lHeight 
            Case iIncY < 0  'Moving Bottom to Top 
                bDone = uaPts(lIdx - 1).Y < 0 And uaPts(lIdx - 2).Y < 0 
        End Select 
        If (lIdx Mod 1000) = 0 Then 
            ReDim Preserve uaPts(UBound(uaPts) + 1000) 
        End If 
    Wend 
     
    'Free excess memory (may have 1001 points dimmed to 2000) 
    ReDim Preserve uaPts(lIdx - 1) 
     
    'Create the array of colors blending from mlColor1 to mlColor2 
    lRet = BlendColors(mlColor1, mlColor2, lIdx / 2, laColors) 
     
    'Now draw each line in it's own color 
    For lIdx = 0 To UBound(uaPts) - 1 Step 2 
        'Move to next point 
        lRet = MoveToEx(hDC, uaPts(lIdx).X, uaPts(lIdx).Y, uTmpPt) 
        'Create the colored pen and select it into the DC 
        hPen = CreatePen(PS_SOLID, 1, laColors(Int(lIdx / 2))) 
        hOldPen = SelectObject(hDC, hPen) 
        'Draw the line 
        lRet = LineTo(hDC, uaPts(lIdx + 1).X, uaPts(lIdx + 1).Y) 
        'Get the pen back out of the DC and destroy it 
        lRet = SelectObject(hDC, hOldPen) 
        lRet = DeleteObject(hPen) 
    Next lIdx 
     
    DrawGradient = lIdx 
     
NormalExit: 
    'Free the memory 
    Erase laColors 
    Erase uaPts 
    Erase uaTmpPts 
    Exit Function 
     
LocalError: 
    MsgBox Err.Description, vbExclamation, "GradientRect.cls" 
    DrawGradient = 0 
    Resume 'NormalExit 
     
End Function 
 
Private Sub RotatePoint(uAxisPt As PointSng, uRotatePt As PointSng, fDegrees As Single) 
 
Dim fDX         As Single 
Dim fDY         As Single 
Dim fRadians    As Single 
 
    fRadians = fDegrees * RADS 
    fDX = uRotatePt.X - uAxisPt.X 
    fDY = uRotatePt.Y - uAxisPt.Y 
    uRotatePt.X = uAxisPt.X + ((fDX * Cos(fRadians)) + (fDY * Sin(fRadians))) 
    uRotatePt.Y = uAxisPt.Y + -((fDX * Sin(fRadians)) - (fDY * Cos(fRadians))) 
     
End Sub 
 
 
 
Private Function Div(ByVal dNumer As Double, ByVal dDenom As Double) As Double 
     
'Divides dNumer by dDenom if dDenom <> 0 
'Eliminates 'Division By Zero' error. 
 
    If dDenom <> 0 Then 
        Div = dNumer / dDenom 
    Else 
        Div = 0 
    End If 
 
End Function 
 
Public Property Let Color1(ByVal lData As Long) 
     
Dim lIdx As Long 
 
    mlColor1 = lData 
    If mlColor1 < 0 Then 
        lIdx = (mlColor1 And Not &H80000000) 
        If lIdx >= 0 And lIdx <= 24 Then 
            mlColor1 = GetSysColor(lIdx) 
        End If 
    End If 
     
End Property 
 
 
Public Property Get Color1() As Long 
    Color1 = mlColor1 
End Property 
 
 
 
Public Property Let Color2(ByVal lData As Long) 
 
Dim lIdx As Long 
 
    mlColor2 = lData 
    If mlColor2 < 0 Then 
        lIdx = (mlColor2 And Not &H80000000) 
        If lIdx >= 0 And lIdx <= 24 Then 
            mlColor2 = GetSysColor(lIdx) 
        End If 
    End If 
 
End Property 
 
 
Public Property Get Color2() As Long 
    Color2 = mlColor2 
End Property 
 
 
 
Public Property Let Angle(ByVal fData As Single) 
     
'Angles are counter-clockwise and may be 
'any Single value from 0 to 359.999999999. 
 
' 135  90 45 
'    \ | / 
'180 --o-- 0 
'    / | \ 
' 235 270 315 
 
    'Correct angle to ensure between 0 and 359.999999999 
    mfAngle = CDbl(fData) - Int(Int(CDbl(fData) / 360#) * 360#) 
 
End Property 
 
 
Public Property Get Angle() As Single 
    Angle = mfAngle 
End Property 
 
 
 
 
 
 
 
Private Sub Class_Initialize() 
 
    mlColor1 = mlDefColor1 
    mlColor2 = mlDefColor2 
    mfAngle = mfDefAngle 
     
End Sub