VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "gdipPathWarper"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
Private Declare Function GdipReversePath Lib "gdiplus" (ByVal path As Long) As Long

' sources for warping and matrix math
' http://www.codeguru.com/cpp/g-m/gdi/gdi/article.php/c3657/
' http://www.java2s.com/Tutorial/VB/0040__Data-Type/Solveequationsusingmatrices.htm

' Note: The class requires two calls (any order) before anything can be rendered to a DC
' 1) Call to SetPathString to set the string to be displayed
' 2) Call to SetPathDest_Points or SetPathDest_Rect to set destination drawing area

' Last but not least. I included options to add rectangles & ellipses. Feel free
' to add code for Lines, Pies, Arcs, Curves, etc, etc.  The SetPathShape_Ellipse and
' SetPathShape_Rectangle functions can be used as templates.

Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)

' GDI+ functions
' ---====| GDI+ Rendering quality |====---
Private Declare Function GdipSetSmoothingMode Lib "gdiplus" (ByVal graphics As Long, ByVal SmoothingMode As SmoothingMode) As Long
Private Declare Function GdipSetInterpolationMode Lib "gdiplus" (ByVal graphics As Long, ByVal interpolation As InterpolationMode) As Long
Private Declare Function GdipSetPixelOffsetMode Lib "gdiplus" (ByVal graphics As Long, ByVal PixOffsetMode As PixelOffsetMode) As Long

' ---====| GDI+ Pens & Brushes |====---
Private Declare Function GdipCreatePen1 Lib "gdiplus" (ByVal Color As Long, ByVal Width As Single, ByVal unit As Long, Pen As Long) As Long
Private Declare Function GdipDeletePen Lib "gdiplus" (ByVal Pen As Long) As Long
Private Declare Function GdipCreateSolidFill Lib "gdiplus" (ByVal argb As Long, Brush As Long) As Long
Private Declare Function GdipDeleteBrush Lib "gdiplus" (ByVal Brush As Long) As Long
Private Declare Function GdipCreateLineBrushFromRectI Lib "gdiplus" (ByRef pRect As RECTL, ByVal color1 As Long, ByVal color2 As Long, ByVal mode As Long, ByVal wrapMode As Long, ByRef lineGradient As Long) As Long

' ---====| GDI+ graphics object |====---
Private Declare Function GdipCreateFromHDC Lib "gdiplus" (ByVal hDC As Long, hGraphics As Long) As Long
Private Declare Function GdipDeleteGraphics Lib "gdiplus" (ByVal graphics As Long) As Long


' ---====| GDI+ Path functions |====---
Private Declare Function GdipDrawPath Lib "gdiplus" (ByVal graphics As Long, ByVal Pen As Long, ByVal path As Long) As Long
Private Declare Function GdipFillPath Lib "gdiplus" (ByVal graphics As Long, ByVal Brush As Long, ByVal path As Long) As Long
Private Declare Function GdipClonePath Lib "gdiplus.dll" (ByVal path As Long, ByRef clonePath As Long) As Long
Private Declare Function GdipCreatePath Lib "gdiplus.dll" (ByVal brushMode As gdiFillMode, ByRef path As Long) As Long
Private Declare Function GdipCreatePath2 Lib "gdiplus.dll" (ByVal pPointsPtr As Long, ByVal typesPtr As Long, ByVal Count As Long, ByVal fillMode As Long, ByRef path As Long) As Long
Private Declare Function GdipDeletePath Lib "gdiplus.dll" (ByVal path As Long) As Long
Private Declare Function GdipGetPathData Lib "gdiplus.dll" (ByVal path As Long, ByVal pDataPtr As Long) As Long
Private Declare Function GdipGetPathFillMode Lib "gdiplus.dll" (ByVal path As Long, ByRef fillMode As Long) As Long
Private Declare Function GdipGetPathPoints Lib "gdiplus.dll" (ByVal path As Long, ByRef pPointsPtr As Long, ByVal Count As Long) As Long
Private Declare Function GdipGetPathWorldBoundsI Lib "gdiplus.dll" (ByVal path As Long, ByRef Bounds As RECTL, ByVal matrix As Long, ByVal Pen As Long) As Long
Private Declare Function GdipGetPointCount Lib "gdiplus.dll" (ByVal path As Long, ByRef Count As Long) As Long
Private Declare Function GdipResetPath Lib "gdiplus.dll" (ByVal path As Long) As Long
Private Declare Function GdipAddPathStringI Lib "gdiplus" (ByVal path As Long, ByVal str As Long, ByVal Length As Long, ByVal family As Long, ByVal Style As Long, ByVal emSize As Single, layoutRect As RECTL, ByVal StringFormat As Long) As Long
Private Declare Function GdipAddPathEllipseI Lib "gdiplus" (ByVal path As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function GdipAddPathRectangleI Lib "gdiplus" (ByVal path As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function GdipAddPathPath Lib "gdiplus" (ByVal path As Long, ByVal addingPath As Long, ByVal pConnect As Long) As Boolean

' ---====| GDI+ String functions |====---
Private Declare Function GdipCreateFontFamilyFromName Lib "gdiplus" (ByVal name As Long, ByVal fontCollection As Long, fontFamily As Long) As Long
Private Declare Function GdipDeleteFontFamily Lib "gdiplus" (ByVal fontFamily As Long) As Long

Private Enum gdiFillMode    ' used for CreatePath
    FillModeAlternate = 0
    FillModeWinding = 1
End Enum
Public Enum gdipWarpModes   ' path warping options
    warpPerspective = 0
    warpBilinear = 1        ' default
    warpSkew = 2
End Enum
Private Enum gdipFontStyles ' used for AddPathString
    FontStyleBold = 1
    FontStyleItalic = 2
    FontStyleUnderline = 4
    FontStyleStrikeout = 8
End Enum
Public Enum LinearGradientMode      ' gradient brush options
    LinearGradientModeHorizontal = 0
    LinearGradientModeVertical = 1
    LinearGradientModeForwardDiagonal = 2
    LinearGradientModeBackwardDiagonal = 3
End Enum

' ---====| graphics quality constants & enumerations |====---
Private Const QualityModeInvalid As Long = -1&
Private Const QualityModeDefault As Long = 0&
Private Const QualityModeLow As Long = 1&
Private Const QualityModeHigh As Long = 2&
Private Enum SmoothingMode
    SmoothingModeInvalid = QualityModeInvalid
    SmoothingModeDefault = QualityModeDefault
    SmoothingModeHighSpeed = QualityModeLow
    SmoothingModeHighQuality = QualityModeHigh
    SmoothingModeNone = QualityModeHigh + 1
    SmoothingModeAntiAlias8x4 = QualityModeHigh + 2
    SmoothingModeAntiAlias = SmoothingModeAntiAlias8x4
    SmoothingModeAntiAlias8x8 = QualityModeHigh + 3
End Enum
Private Enum InterpolationMode
   InterpolationModeInvalid = QualityModeInvalid
   InterpolationModeDefault = QualityModeDefault
   InterpolationModeLowQuality = QualityModeLow
   InterpolationModeHighQuality = QualityModeHigh
   InterpolationModeBilinear
   InterpolationModeBicubic
   InterpolationModeNearestNeighbor
   InterpolationModeHighQualityBilinear
   InterpolationModeHighQualityBicubic
End Enum
Private Enum PixelOffsetMode
   PixelOffsetModeInvalid = QualityModeInvalid
   PixelOffsetModeDefault = QualityModeDefault
   PixelOffsetModeHighSpeed = QualityModeLow
   PixelOffsetModeHighQuality = QualityModeHigh
   PixelOffsetModeNone    ' No pixel offset
   PixelOffsetModeHalf     ' Offset by -0.5 -0.5 for fast anti-alias perf
End Enum

' ---====| Enumerations |====---
Public Enum ePathOrder
    TopLeft_Clockwise = 0 ' left-top, right-top, right-bottom, left-bottom
    TopLeft_zPattern = 1  ' left-top, right-top, left-bottom, right-bottom
End Enum
Public Enum ePathPoint
    TopLeft = 0
    TopRight = 1
    BottomLeft = 2
    BottomRight = 3
End Enum
Private Enum StatusCodes
    statTextApplied = 1
    statBoundsApplied = 2
    statCanDraw = 3
    statRecalcWarp = 4
    statInvalid = 8
End Enum

' ---====| User-Defined Types |====---
Private Type PointF ' used by many GDI+ functions
   X As Single
   Y As Single
End Type
Private Type PathData   ' used to retrieve path points & types
   Count As Long
   Points As Long ' pointer to array of PointF
   Types As Long ' pointer to array of gdiPathTypes
End Type
Private Type RECTL  ' used by many GDI+ functions
   X As Long
   Y As Long
   mWidth As Long
   mHeight As Long
End Type
Private Type PenBrushData   ' cached path information
    Pen As Long         ' pen handle -- must be destroyed
    Brush As Long       ' brush handle -- must be destroyed
    PenColor As Long    ' pen/outline color
    color1 As Long      ' solid brush color or 1st gradient color
    color2 As Long      ' second gradient color
    GradDirection As LinearGradientMode
    GradBounds As RECTL ' latest warp bounds
End Type

' ---====| Class variables |====---
Private m_WarpFactors() As Single   ' calc factors for manual warping
Private m_Style As gdipWarpModes    ' current warp option
Private m_Path As Long              ' handle to a Path -- must be destroyed
Private m_SrcPts() As PointF        ' rect coordinates of non-warped path
Private m_DestPts() As PointF       ' coordinates of warped path
Private m_Status As StatusCodes     ' class-only status flags
Private m_FillObject As PenBrushData    ' GDI+ objects, pen/brush colors, etc

Public Function SetPathString(Text As String, Font As StdFont, _
                            Optional X As Long, Optional Y As Long, _
                            Optional hDC As Long, Optional ResetPath As Boolean = True) As Boolean

    ' if function returns zero; failed to create path from passed Font -- true type fonts only!
    
    ' Text is the formatted text, including vbCrLf characters if appropriate
    ' Font is the font to be used with Text. Use TrueType fonts
    ' X & Y are where in the path the string should start
    ' hDC if provided will draw the updated path to the passed DC
    
    Dim fRect As RECTL, fontFam As Long, fontStyle As Long
    Dim tPath As Long
    
    ' get font style from passed font object
    With Font
        fontStyle = (Abs(.Italic) * FontStyleItalic) Or (Abs(.Strikethrough) * FontStyleStrikeout) _
        Or (Abs(.Underline) * FontStyleUnderline) Or (Abs(.Bold) * FontStyleBold)
    End With

    ' create new path and add text as the path
    Call GdipCreateFontFamilyFromName(StrPtr(Font.name), 0, fontFam)
    If fontFam Then
        If GdipCreatePath(FillModeAlternate, tPath) = 0& Then
            fRect.X = X: fRect.Y = Y
            Call GdipAddPathStringI(tPath, StrPtr(Text), -1, fontFam, fontStyle, Font.Size, fRect, 0&)
            GdipDeleteFontFamily fontFam
            SetPathString = AppendPath(tPath, ResetPath, hDC)
        Else
            GdipDeleteFontFamily fontFam
        End If
    End If
End Function

Public Function SetPathShape_Rectangle(Left As Long, Top As Long, _
                            Right As Long, Bottom As Long, _
                            Optional hDC As Long, Optional ResetPath As Boolean = True) As Boolean

    ' if function returns false; failed to create path
    
    ' Left & Top are where the rectangle starts
    ' Right & Bottom are where it ends
    ' hDC if provided will draw the updated path to the passed DC
    
    Dim tPath As Long
        
    If GdipCreatePath(FillModeAlternate, tPath) = 0& Then
        GdipAddPathRectangleI tPath, Left, Top, Right - Left + 1, Bottom - Top + 1
        If AppendPath(tPath, ResetPath, 0&) Then
            SetPathDest_Rect Left, Top, Right, Bottom, hDC
            SetPathShape_Rectangle = True
        End If
    End If

End Function

Public Function SetPathShape_Ellipse(Left As Long, Top As Long, _
                            Right As Long, Bottom As Long, _
                            Optional hDC As Long, Optional ResetPath As Boolean = True) As Boolean

    ' if function returns false; failed to create path
    
    ' Left & Top are where the ellipsis starts
    ' Right & Bottom are where it ends
    ' hDC if provided will draw the updated path to the passed DC
    
    Dim tPath As Long
        
    If GdipCreatePath(FillModeAlternate, tPath) = 0& Then
        GdipAddPathEllipseI tPath, Left, Top, Right - Left + 1, Bottom - Top + 1
        If AppendPath(tPath, ResetPath, 0&) Then
            SetPathDest_Rect Left, Top, Right, Bottom, hDC
            SetPathShape_Ellipse = True
        End If
    End If

End Function

Public Sub SetPathDest_Points(Points() As Single, ByVal Order As ePathOrder, Optional ByVal hDC As Long)
    
'    // warp points are arranged in Z-configuration internally
'    //
'    //      0 ------- 1
'    //      |         |
'    //      |         |
'    //      2 ------- 3
'    //
    
    ' Points must be a 2D array: (0 to 1, 0 to 3)
    ' If Order = TopLeft_Clockwise then point order is
    '   (0,0)=TopLeft-X     (1,0)=TopLeft-Y
    '   (0,1)=TopRight-X    (1,1)=TopRight-Y
    '   (0,2)=BotRight-X    (1,2)=BotRight-Y
    '   (0,3)=BotLeft-X     (1,3)=BotLeft-Y
    ' if Order = TopLeft_zPattern then
    '   (0,0)=TopLeft-X     (1,0)=TopLeft-Y
    '   (0,1)=TopRight-X    (1,1)=TopRight-Y
    '   (0,2)=BotLeft-X     (1,2)=BotLeft-Y
    '   (0,3)=BotRight-X    (1,3)=BotRight-Y
    ' hDC if provided will draw the updated path to the passed DC
    On Error GoTo EH
    
    If UBound(Points, 2) = 3 And UBound(Points, 1) = 1 Then
        If LBound(Points, 2) = 0 And LBound(Points, 1) = 0 Then
            If Order = TopLeft_zPattern Then
                CopyMemory m_DestPts(0), Points(0, 0), 32&
            ElseIf Order = TopLeft_Clockwise Then
                CopyMemory m_DestPts(0), Points(0, 0), 16&
                CopyMemory m_DestPts(3), Points(2, 0), 8&
                CopyMemory m_DestPts(2), Points(3, 0), 8&
            Else
                Exit Sub
            End If
            If m_Style = warpSkew Then
                m_DestPts(3).X = m_DestPts(1).X + (m_DestPts(2).X - m_DestPts(0).X)
                m_DestPts(3).Y = m_DestPts(1).Y + (m_DestPts(2).Y - m_DestPts(0).Y)
            End If
            BuildWarpMatrix hDC
        End If
    End If
EH:
    If Err Then Err.Clear ' passed wrong dimension array
End Sub

Public Sub SetPathDest_Rect(rectLeft As Long, rectTop As Long, _
                        rectRight As Long, rectBottom As Long, Optional hDC As Long)

    ' // warp points are arranged in Z-configuration -- See SetPathDest_Points routine
    ' hDC if provided will draw the updated path to the passed DC
    
    m_DestPts(0).X = rectLeft: m_DestPts(0).Y = rectTop
    m_DestPts(1).X = rectRight: m_DestPts(1).Y = rectTop
    m_DestPts(2).X = rectLeft: m_DestPts(2).Y = rectBottom
    m_DestPts(3).X = rectRight: m_DestPts(3).Y = rectBottom
    BuildWarpMatrix hDC

End Sub

Public Function UpdateDestPoint(ByVal X As Single, ByVal Y As Single, ByVal whichPoint As ePathPoint, Optional hDC As Long) As Boolean
    ' update a single point in the warp bounds
    ' hDC if provided will draw the updated path to the passed DC
    
    ' when skewing, the warp shape must be a parallelogram. This requires calculating
    ' and also modifying the opposite corner point from the one being updated
    If (m_Status And statBoundsApplied) = statBoundsApplied Then
        Select Case whichPoint
        Case TopLeft, BottomRight ' pts 0,3 modifies 3,0
            m_DestPts(whichPoint).X = X: m_DestPts(whichPoint).Y = Y
            If m_Style = warpSkew Then
                m_DestPts(3 - whichPoint).X = m_DestPts(Abs(whichPoint - 1)).X + m_DestPts(Abs(whichPoint - 2)).X - m_DestPts(whichPoint).X
                m_DestPts(3 - whichPoint).Y = m_DestPts(Abs(whichPoint - 1)).Y + m_DestPts(Abs(whichPoint - 2)).Y - m_DestPts(whichPoint).Y
            End If
            BuildWarpMatrix hDC
            UpdateDestPoint = True
        
        Case TopRight, BottomLeft ' pts 1,2 modifies 2,1
            m_DestPts(whichPoint).X = X: m_DestPts(whichPoint).Y = Y
            If m_Style = warpSkew Then
                m_DestPts(3 - whichPoint).X = m_DestPts((whichPoint - 1) * 3).X + m_DestPts((2 - whichPoint) * 3).X - m_DestPts(whichPoint).X
                m_DestPts(3 - whichPoint).Y = m_DestPts((whichPoint - 1) * 3).Y + m_DestPts((2 - whichPoint) * 3).Y - m_DestPts(whichPoint).Y
            End If
            BuildWarpMatrix hDC
            UpdateDestPoint = True
        End Select
    End If
End Function

Public Function OffsetDestination(ByVal Xoffset As Single, ByVal Yoffset As Single, Optional hDC As Long) As Boolean

    ' function shifts the path by X,Y offsets
    ' hDC if provided will draw the updated path to the passed DC
    
    If (m_Status And statBoundsApplied) = statBoundsApplied Then
        On Error Resume Next ' errors? Possibly if user sets huge offset & overflow occurs
        Dim I As Long, tPts() As PointF
        tPts() = m_DestPts()
        For I = 0 To 3
            m_DestPts(I).X = m_DestPts(I).X + Xoffset
            m_DestPts(I).Y = m_DestPts(I).Y + Yoffset
        Next
        If Err Then
            Err.Clear
            m_DestPts = tPts
        Else
            BuildWarpMatrix hDC
            OffsetDestination = True
        End If
    End If
End Function

Public Property Let WarpStyle(Style As gdipWarpModes)
    ' option to set the warp mode/style
    If Style >= warpPerspective And Style <= warpSkew Then
        If Not Style = m_Style Then
            m_Style = Style
            If m_Style = warpSkew Then
                ' UpdateDestPoint will recalc the bottom right corner as needed
                UpdateDestPoint m_DestPts(0).X, m_DestPts(0).Y, TopLeft
            Else
                BuildWarpMatrix 0&
            End If
        End If
    End If
End Property
Public Property Get WarpStyle() As gdipWarpModes
    WarpStyle = m_Style
End Property

Public Sub SetOutLine(ByVal PenThickness As Single, ByVal PenColor As Long, Optional ByVal Opacity As Long = 100)
    ' Outline pen color or null color
    ' Pass -1 for PenColor to assign a null pen
    If m_FillObject.Pen Then GdipDeletePen m_FillObject.Pen
    m_FillObject.Pen = 0&
    m_FillObject.PenColor = PenColor
    If PenColor > -1 Then
        If CreateBGRobject(m_FillObject.PenColor, Opacity) Then
            GdipCreatePen1 m_FillObject.PenColor, PenThickness, 2, m_FillObject.Pen
        End If
    End If
End Sub

Public Sub SetBrush(ByVal Color As Long, Optional ByVal gradientColor As Long = -1, Optional gradientDirection As LinearGradientMode, Optional ByVal Opacity As Long = 100)
    ' Fill brush, either solid, gradient or null
    ' Pass -1 for Color to assign a null brush
    With m_FillObject
        If .Brush Then GdipDeleteBrush .Brush
        .Brush = 0&
        .color1 = Color
        .GradDirection = -1 ' non-gradient brush, default
        If .color1 > -1 Then
            If CreateBGRobject(.color1, Opacity) Then
                .color2 = gradientColor
                If .color2 > -1 Then
                    If CreateBGRobject(.color2, Opacity) Then
                        .GradDirection = gradientDirection
                        If (m_Status And statRecalcWarp) = 0& Then
                            GdipCreateLineBrushFromRectI .GradBounds, .color1, .color2, .GradDirection, 0&, .Brush
                        End If
                    End If
                End If
                If .GradDirection = -1 Then ' use solid brush
                    GdipCreateSolidFill .color1, m_FillObject.Brush
                End If
            End If
        End If
    End With
End Sub

Public Sub GetBoundingRect(Left As Long, Top As Long, Right As Long, Bottom As Long)
    ' Retrieves the bounding rectangle of the current warped path
    If m_Path = 0& Then
        Left = 0&: Top = 0&
        Right = 0&: Bottom = 0&
    Else
        Dim tPath As Long, srcPts() As Single, ptTypes() As Byte
        If (m_Status And statRecalcWarp) = statRecalcWarp Then
            tPath = WarpPoints(srcPts, ptTypes)
            If tPath Then GdipDeletePath tPath
        End If
        With m_FillObject
            Left = .GradBounds.X: Top = .GradBounds.Y
            Right = .GradBounds.mWidth + Left
            Bottom = .GradBounds.mHeight + Top
        End With
    End If

End Sub

Public Function GetBoundingPoints(Points() As Single) As Long

'   If function retuns zero, the points array should be considered null
'       Array is returned as 2D :: (0 to 1, 0 to nrPoints-1)
'       The 1st element of 1st dim are X values, the 2nd are Y values
'       There are always four points in the array

'    // bouding points are arranged in Z-configuration
'    //
'    //      0 ------- 1
'    //      |         |
'    //      |         |
'    //      2 ------- 3
'    //
    If (m_Status And statBoundsApplied) = statBoundsApplied Then
        ReDim Points(0 To 1, 0 To 3)
        CopyMemory Points(0, 0), m_DestPts(0), 32&
        GetBoundingPoints = 4
    End If
End Function

Public Function GetPathPoints(Points() As Single, PointType() As Byte) As Long
        ' returns the path points & path types
        ' Points when returned is 2D array: (0 to 1, 0 to nrPts-1)
        '       the X coords are 1D 0 element, Y is 1 element
        ' PointType are members of the GDI+ documented PathPointType enumeration
        ' If function returns 0 consider arrays null
        If m_Path Then
            Dim tPath As Long, Count As Long
            tPath = WarpPoints(Points, PointType, Count)
            If tPath Then GdipDeletePath tPath
            GetPathPoints = Count
        End If

End Function


Private Function AppendPath(fromPath As Long, bReset As Boolean, hDC As Long) As Boolean

    Dim fRect As RECTL
    
    m_Status = m_Status Or statRecalcWarp Or statTextApplied
    
    If m_Path = 0& Then
        m_Path = fromPath
    Else
        If bReset Then GdipResetPath m_Path
        GdipAddPathPath m_Path, fromPath, True
        GdipDeletePath fromPath
    End If
    ' get the actual bounds of the path before any warping is applied
    GdipGetPathWorldBoundsI m_Path, fRect, 0&, 0&
    m_SrcPts(0).X = fRect.X: m_SrcPts(0).Y = fRect.Y
    m_SrcPts(1).X = fRect.mWidth + fRect.X: m_SrcPts(1).Y = m_SrcPts(0).Y
    m_SrcPts(2).X = m_SrcPts(0).X: m_SrcPts(2).Y = fRect.mHeight + m_SrcPts(0).Y
    m_SrcPts(3).X = m_SrcPts(1).X: m_SrcPts(3).Y = m_SrcPts(2).Y
        
    ' recalculate the warp matrix only if destination points have been set
    If (m_Status And statBoundsApplied) = statBoundsApplied Then BuildWarpMatrix hDC
    AppendPath = True
    
End Function
Private Function CreateBGRobject(Color As Long, ByVal Opacity As Long) As Boolean
    ' Local use to create RGB color to BGRA
    If Opacity < 1 Then
        Exit Function
    ElseIf Opacity > 100 Then
        Opacity = 100
    End If
    Opacity = (255 * Opacity) \ 100
    Color = ((Color And &H7ThenrrTo 3+ty < 1 Then
   slBxit FunctoSCThen) Opa * Opaa WarpPointen
  +k0n poAltecalcula * Opaa Wad Function
en
  * OElseIf Opacity  2 ------- 3
'         LecR PoimoothingMrnd If

nerRa-)d Functiono,reateWatTeed Then BuildApoAltecalculaeIf OpoaTo 3+ty < l draw umembers the warp modeoAltecalc
    
End Fucal RGB colllllllouldfce(alc
    
End Fucal RGB c< 1 Then
   sln
e warpf
    Enevers the wagawarpf
  sI ' Local use to create RGB color to B-embero Nimo warpf
    Enevro Nimo w a+Functi 
    ' recalcula
    EnePts(0 draw the updated  a+ eIf Opaci-      Opa'ee.local usecti 
 1D 0 ti rovided will draw the updated path to the passed DC
    
    If (m_Status And statBoundsApplied) = statBoundsApplied Then
        On Error Resume Next ' errors? Possibly if user sets he(alc
    
End Fucal RGB c< 1 Then
   Ith poin',ctioTo 3Rect.Y
    m_Sbrl e pen conten'user sets ha-)d FunctinrrToal RGB c< 1 T# And statBokts ha-)w umembers tu m_SrcPts(0).Y
ts ha-)w umembers tu X2D)epaci-umemberse   // c< 1< 1 T#           m_Sr.X = m_S atng, Top As Long, Ri soRect.Y1& rrTo 3+ty  Enevers tcal RGot + 1, Bottom ).X eIf OmRe Functioyr + 1,     ' reca2).Y = fRectF reca2).Y = fRec'5 by  Opacity tBound = (25(nd = (me ;ba      Opacity tBound = (earGradientMode   
End Fucal RGB c< 1 Thena  Entioyr + 1, RGB c<  
         f to B by ca2).Y oculaeIf OpoaTo 3+ty <tfal RGB e ;ba  oaTo 3ctF rcalc
kdientMode   llllllllllllllllllllllllllllcreate RGB color to B-embero Nir)c
kdientMode   l0dipi<ateWa= m_Destvero; failednBrxtsl2).Y.atrieves the bounding recAc
ka2).Y ocugl pending p AppendPath = True
    
End Function
Private Function CreateBGRobject(Color As Long, ByVal Opacity As Long) As Boolean
    ' Local use toecalxns the city
   End If
    En_Destv tu m_SrcPtssssssssssss,ate FucPtssssss    mn
    ' Local use toea    e RGB color to B-emto ns the2Applied) hfRec'5lcula
    Enie BuildWarpMa up toea  gl pending citysaemo ns the2Applied) hfR
        End sssss) hfR
8khPoint).ied) (ip OpoaTo 3+tyt, 0&, 0&ip OpoaTo 3+tyt,0&, 0&
    m_SmoaTo 3+tyt, 0&, 0&ip OpoaTo&, 0 1,  3+tyt, -ual bounds of '_S atng,onal hDC3+tyt,0&, 0&
tyt,0&, 0&aemo n).X3+tythe2Ap -updated pa&ae (ip OpoaTo 3+t+tythe2Ap -ythe2Ap -ythe2+e0ythe2Ap Ap -ythe2+e0 atng,onal hDC3+tyt,0&0&, 0&atng,onal hDC3+tyt,0&0&, 0&a>onal ersyr +e0ythe2A path to)N have been d  f to B by ca2).Y oculaeIf OpoaTo 3+ty <tfal RGB e ;ba  oaTo 3ctF rcalinn ottom = .GradBounds.mHeighunds.mHeighunds.mHeignal ms((p Or statTextApplied
 &, 0&aeignal mspending citysaestaAp Ap -ythe2+e0 atng,onal hD 0&, 0&ip &, )ml h)GRobj7X coords are 1s(2).Y =aoysaestaAp = TopLeft_Clockwise .GradBokHeignD, )ml h)GRobj7X coortoea   Else
      Left =eie   Left =eie   rns 0 consider ar&eml h)GRobj7X co;ba  oaTo 3ctF he2Apm_SrcPts(1).X = fRe    ErEtApplieYCe2Apm_SrcPts(1)>fRe eft =eie   rns 0 consider ar&eml hs he1m h)GRobj7X coorx &, 0&aeibjeTopLeft_ClockriyncteH+ (m_ ((Color And yh
     onathPoi'7r+ on7.X    rns 0 cgilor