VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "clsRegions"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

' A general purpose Regions class with 99% of all known region functions I can
' think of and some that are personally created that are not provided by API.
' Most are easy, some are advanced. Read thru the remarks in this section for
' an overview of the entire class and the functions that this class offers.

' Ref for Regions: http://msdn.microsoft.com/library/en-us/gdi/regions_6o6r.asp

' BRIEF HISTORY

' \/\/\/\/\/\/\
' *** VERSION 1 - used GetPixel & CombineRgn, just like everyone else
' *** VERSION 2 - use of DIBs  vs GetPixel & my unique method (Jun 04)
' UPDATED to include using DIB sections. The increase in speed is
' truly amazing over earlier version.....
' Over a 300% increase in speed noted on smallish bitmaps (96x96) &
' Over a 425% increase noted on mid-sized bitmaps (265x265)

' This approach should be somewhat faster than typical approaches that
' create a region by using CreateRectRgn, CombineRgn & DeleteObject.
' That is 'cause this approach does not use any of those functions to
' create regions (except in rare cases on Win98 and when stretching a
' region. Even in those rare cases, those functions are only called once
' for every 2000 region parts). Like typical approaches a rectangle format
' is used to "add to a region", however, this approach directly creates
' the region header & region structure and passes that to a single API
' to finish the job of creating the end result. For those that play around
' with such things, I think you will recognize the difference.

' EDITED: The ExtCreateRegion seems to have an undocumented restriction: it
' won't create regions comprising of more than 4K rectangles (Win98). So to
' get around this for very complex bitmaps, I rewrote the function to create
' regions of 2K rectangles at a time if needed. This is still extremely
' fast. I compared the window shaping code from vbAccelerator with Win98's
' SandStone.bmp (15,000 rects) & using Win98. vbAccelerator's code averaged
' 6,900 ms. My routines averaged 77 ms, uncompiled, & that's not a typo!

' EDITED: I was allowing default error trapping on UBound() to resize the
' rectangle array: when trying to update array element beyond UBound, error
' would occur & be redirected to resize the array. However, thanx to
' Robert Rayment, if the UBound checks are disabled in compile optimizations,
' then we get a crash. Therefore, checks made appropriately & a tiny loss of
' speed is the trade-off for safety.

' *** VERSION 3.1 - Anti Regions & speed modifications (10 Jan 05)
' Function has an optional parameter to return the anti-Region.
' That is the region of only transparent pixels. This could be used, for
' example, with APIs like FillRgn to replace the "transparent" color with
' another color.

' **** VERSION 4 (Oct 05) - Accessed bitmap bytes directly within memory.
' This version was tossed. Although the time saved on very large bitmaps was
' somewhat significant, the routines were not robust enough to handle all the
' various exceptions within bitmap structures (4bpp, 8pbb, compression, etc)
' Something on the backburner to possibly attack at a later time.

' -- Converted from Module to Class and added other methods
'    Note: This class does not store any regions or pointers. Regions that
'          you create from this class must be destroyed by you. There is only
'          one exception: Should you apply/assign a region to a window, then
'          you must not destroy that region; Windows O/S owns it thereafter

'   About some functions. Not all region-related functions are included here.
'   Feel free to add others as you find you want them. Some of the functions
'   are one-liners, meaning all they do is call an API and no other processing.
'   Those functions may not be of common-knowledge or not often used. Adding
'   them to this class was just a matter of courtesy vs necessity.

' *** VERSION 4.1 (Nov 05) - Included exclusion rectangles
' By excluding a portion of your bitmap that will never have "transparent" pixels
' in it, you can help speed up the process even more. This is simply because the
' routines will just blindly add that rectangular area to the shaped region without
' the need to compare any pixel in that area. The time savings is directly
' proportional to the area of the rectangle & size of the bitmap. The larger
' the bitmap and the larger the exclusion rectangle; the larger the time savings.
' Example: a 200x200 pixel rectangular exception area: 120,000 fewer bytes processed
'            200 x 200 x 3 bytes per pixel
' -- modified some core routines to accept an XForm structure (for stretching regions)

' Feb 1 & 2. Added following functions to help make this a complete Regions class:
'   CombineRegions, CreateRegion, IsNullRegion, PaintRegion
' -- also added capability for user to pass RECT & POINTAPI structures to applicable
'       functions when the undocumented VB VarPtr() function is used
' -- Added capability of allowing class to store a region.
'       This way you don't need to maintain a separate variable for that purpose.
'       When the class is destroyed, if the region still exists, it will be destroyed too.
'       See SetCachedRegion function & GetCachedRegion property
' -- for the fun of it. Added a good, flexible, region intersection routine


'========================================================================================
'                 PUBLIC FUNCTION LISTING FOR THIS CLASS
'                 see each function for more information
'========================================================================================
' AreReqionsEqual :: compares 2 regions and returns if they are equal or not **
' CombineRegions :: merges 2 regions with various options **
' ConvertRgnToRectangle :: changes any region into a simple rectangular region **
' CreateRegion :: mimics CreateRectRgn,CreateEllipticRgn,CreatePolygonRgn, & more ++
' DestroyRegion :: removes a region from memory **
' ExtractRegionRectangles :: returns region's internal rectangles as a byte array
' GetCachedRegion (Property) :: returns region handle that was set via SetCachedRegion
' GetRegionBytes :: returns the entire region (header too) as a byte array
' ImportRegion :: offers 8 ways of creating a region. Also see RegionFromBitmap ++
' IsNullRegion :: returns if a region contains a valid bounding rectangle
' IsRegion :: returns whether or not a passed handle is a region
' MoveRegion :: relocates/offsets a region or clipping region
' PaintRegion :: fills, frames and/or inverts a DC based off of a region
' PointInRegion :: returns if a passed X,Y is within a region or clipping region **
' PolyFillMode :: sets a region's polygon fill mode. See MSDN for more info.
' RectInRegion :: returns if a rectangle is within a region or clipping region **
' RegionBounds :: returns the bounding rectangle of a region **
' RegionFromBitmap :: returns a region based off of a bitmap's image ++
' RegionIntersect :: tests 2 regions for intersection/collision ++
' RegionSizeBytes :: returns the size, in bytes, of a region **
' SaveRegionToFile :: saves the region data (header too) to a file
' SetCachedRegion :: allows user to have class save & manage one region handle
' SetClipRegionToDC :: applies or removes a clipping region to/from a DC ** &&
' SetRegionToWindow :: applies or removes a region to/from a window **
' StretchRegion :: stretches a region much like StretchBlt does to bitmaps ++
' ValidateInvalidateRgn :: validates or invalidates a window based of an optional region **

' ** Those functions ending with ** are basically one liners
'    that can be called directly from APIs, should you choose
' ++ May return a region that must be destroyed at some point, unless
'    applied to a window via SetWindowRgn API or class's SetRegionToWindow
' && Regions applied as clipping regions are not actually selected into DC,
'    a copy is; therefore, originals must be destroyed at some point
'========================================================================================

' FYI: What is a region? Regions are, memory-wise, stacked rows of rectangles where
' each rectangle can be of any width, but any on a single row are all the same height,
' no row of rectangles will overlap another row of rectangles and the rows of
' rectangles are top to bottom, left to right, but each row can be of any height.
' Regional rectangles are like the bricks of a house and the house is the region.
' The method employed here builds those rows of bricks and then passes all the rows
' (the entire blueprint) to a single API to create a region/house in a single call.
' This is much faster than others out there that basically build a single brick and
' then add it to the house, a brick at a time (slow) or a row at a time (better).

' Last note: Should you want to strip out only the RegionFromBitmap routine, include:
'   RegionFromBitmap
'   c_CreatePartialRegion
'   c_CreateWin9xRegion
'   c_ScanSection
'   appropriate APIs, Constants, & User Defined Types (UDTs) for those routines

' GDI32 APIs
Private Declare Function CombineRgn Lib "gdi32.dll" (ByVal hDestRgn As Long, ByVal hSrcRgn1 As Long, ByVal hSrcRgn2 As Long, ByVal nCombineMode As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Private Declare Function ExtCreateRegion Lib "gdi32.dll" (ByRef lpXform As Any, ByVal nCount As Long, lpRgnData As Any) As Long
' ^^ modified lpXform parameter to accept an XForm or not
Private Declare Function GetDIBits Lib "gdi32.dll" (ByVal hDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Any, lpBI As BITMAPINFO, ByVal wUsage As Long) As Long
Private Declare Function GetGDIObject Lib "gdi32.dll" Alias "GetObjectA" (ByVal hObject As Long, ByVal nCount As Long, lpObject As Any) As Long
' Kernel32 APIs
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
' User32 APIs
Private Declare Function GetDC Lib "user32.dll" (ByVal hWnd As Long) As Long
Private Declare Function GetSysColor Lib "user32.dll" (ByVal nIndex As Long) As Long 'also used in clsBarColors
Private Declare Function IsWindow Lib "user32.dll" (ByVal hWnd As Long) As Long
Private Declare Function ReleaseDC Lib "user32.dll" (ByVal hWnd As Long, ByVal hDC As Long) As Long
Private Declare Function SetRect Lib "user32.dll" (lpRect As RECT, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
Private Declare Function SetWindowRgn Lib "user32.dll" (ByVal hWnd As Long, ByVal hRgn As Long, ByVal bRedraw As Boolean) As Long

Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type
Private Type BITMAPINFOHEADER '40 bytes
    biSize As Long
    biWidth As Long
    biHeight As Long
    biPlanes As Integer
    biBitCount As Integer
    biCompression As Long
    biSizeImage As Long
    biXPelsPerMeter As Long
    biYPelsPerMeter As Long
    biClrUsed As Long
    biClrImportant As Long
End Type
Private Type BITMAPINFO
    bmiHeader As BITMAPINFOHEADER
    bmiColors() As Long
End Type
' options for use with CombineRegions function
Public Enum RegionOps
    RGN_NoAction = 0
    RGN_AND = 1
    RGN_COPY = 5
    RGN_DIFF = 4
    RGN_OR = 2
    RGN_XOR = 3
End Enum
' Constants used
Private Const BI_RGB As Long = 0&
' common to a few sub-functions, these are erased immediately after use/reuse:
Private rgnRects() As RECT  ' array of rectangles comprising region
Private gpBytes() As Byte   ' the DIB byte array

Public Function RegionFromBitmap(ByVal hBitmap As Long, _
        Optional ByVal hWndToApply As Long, _
        Optional ByVal transColor As Long = -1&, _
        Optional ByVal returnAntiRegion As Boolean, _
        Optional ByVal ExcludeL As Long, Optional ByVal ExcludeT As Long, _
        Optional ByVal ExcludeR As Long, Optional ByVal ExcludeB As Long) As Long

'*******************************************************
' FUNCTION RETURNS.
' 1. If hWndToApply is zero, the shaped region handle is returned
' 2. Otherwise, a non-zero value indicates region created & applied
'    to the passed window handle
'*******************************************************

' Also see CreateRegion & ImportRegion for other ways to create/re-use regions

' PARAMETERS
'=============
' hBitmap (Required) : handle to a bitmap to be used to create the region
' Optional hWndToApply : hWnd to assign the shaped region to
' Optional transColor : the transparent color, if -1, then top/left corner is used
' Optional returnAntiRegion : If False (default) then the region excluding transparent
'       pixels will be used/returned.  If True, then the region including only
'       transparent pixels will be used/returned
' Optional ExcludeL, ExcludeT, ExcludeR, ExcludeB are exclusion rectangle coordinates
'   If provided, the pixels in those coordinates will not be examined and
'   that rectangle's area will be added to the shaped region as is. This exclusion
'   rectangle should be as large of an area of the image as possible
'   which contains no transparency

    ' test for required variable first
    If hBitmap = 0& Then Exit Function
    ' if applying to a window, ensure the value passed is a window
    If hWndToApply <> 0& Then
        If IsWindow(hWndToApply) = 0& Then Exit Function
    End If
    
    ' now ensure hBitmap handle passed is a usable bitmap
    Dim bmpInfo As BITMAPINFO
    If GetGDIObject(hBitmap, Len(bmpInfo), bmpInfo) = 0& Then Exit Function
    
    ' declare bunch of variables...
    Dim rectCount As Long ' number of rectangles & used to increment above array
    Dim lScanLines As Long ' used to size the DIB bit array
    Dim rtnRegion As Long ' region handle returned by this function if appropriate
    Dim dibDC As Long ' DC to use for GetDIBits
   
    On Error GoTo CleanUp
      
    With bmpInfo.bmiHeader
    
        .biHeight = Abs(.biHeight) ' per msdn the .biHeight may be negative already
        
        ' Scans must align on dword boundaries:
        lScanLines = (.biWidth * 3& + 3&) And &HFFFFFFFC
        ReDim gpBytes(0 To lScanLines - 1&, 0 To .biHeight - 1&)
        
        ' validate passed exclusion rectangle dimensions
        If ExcludeL < 0& Then ExcludeL = 0&
        If ExcludeT < 0& Then ExcludeT = 0&
        If ExcludeB > .biHeight Then ExcludeB = .biHeight
        If ExcludeR > .biWidth Then ExcludeT = .biWidth
        If ExcludeB <= ExcludeT Or ExcludeR <= ExcludeL Then
            ExcludeR = ExcludeL
            ExcludeB = ExcludeT
        End If
       
        ' build the DIB header
       .biSize = Len(bmpInfo.bmiHeader)
       .biBitCount = 24
       .biPlanes = 1
       .biCompression = BI_RGB
       .biClrUsed = 0&
       .biClrImportant = 0&
       .biSizeImage = 0&
       .biHeight = -.biHeight
        ' ^^ most DIBs are bottom:top, by using negative Height it will load top:bottom
    End With
    
    ' get the image into DIB bits,
    dibDC = GetDC(0&)
    ' note that biHeight above was changed to negative so we reverse it form here
    If GetDIBits(dibDC, hBitmap, 0&, -bmpInfo.bmiHeader.biHeight, gpBytes(0, 0), bmpInfo, 0&) = 0& Then
        Erase gpBytes()
        ReleaseDC 0&, dibDC
        Exit Function
    End If
    ReleaseDC 0&, dibDC 'failure to release DC can leak or removes limited shared DCs
        
    ' now calculate the transparent color as needed
    If transColor = -1 Then
        ' when -1 is passed, use top left corner pixel color
        transColor = gpBytes(0, 0) Or (gpBytes(1, 0) * 256&) Or (gpBytes(2, 0) * 65536)
    Else
        ' convert vbSystemColor if possible
        If transColor < 0& Then transColor = GetSysColor(transColor And &HFF&)
        ' typical DIBs are stored as BGR vs RGB
        ' convert to BGR vs converting each bitmap pixel to RGB for comparison
        transColor = ((transColor And &HFF&) * &H10000) Or _
            (((transColor And &HFF00&) \ &H100&) * &H100&) Or _
            ((transColor And &HFF0000) \ &H10000)
    End If
        
    ' Process the bitmap bytes
    With bmpInfo.bmiHeader
     
        .biHeight = Abs(.biHeight)
         ' start with an arbritray number of rectangles
        ReDim rgnRects(0 To .biWidth * 3)
            
        If ExcludeR > ExcludeL Then
            ' when an exclusion rectangle is provided, we process the bitmap like so:
            
            ' Do from the top of bitmap to top of exclusion rectangle
            c_ScanSection 0&, .biWidth, .biWidth, .biWidth, ExcludeT, transColor, returnAntiRegion, rectCount
            
            ' Do the exclusion rectangle, add estimated number of unexamined rows
            ReDim Preserve rgnRects(0 To UBound(rgnRects) + (ExcludeB - ExcludeT))
            c_ScanSection ExcludeT, ExcludeL, ExcludeR, .biWidth, ExcludeB, transColor, returnAntiRegion, rectCount
            
            ' Do from bottom of the exclusion rectangle to the bottom of the bitmap
            c_ScanSection ExcludeB, .biWidth, .biWidth, .biWidth, .biHeight, transColor, returnAntiRegion, rectCount
    
        Else
            ' No exclusion rectangle, process from top to bottom of the bitmap
            c_ScanSection 0&, .biWidth, 0&, .biWidth, .biHeight, transColor, returnAntiRegion, rectCount
        
        End If
        
    End With
    
    On Error Resume Next
    ' check for failure & engage backup plan if needed
    If rectCount Then
    
        ' there were regional rectangles identified, try to create the region
        rtnRegion = c_CreatePartialRegion(2&, rectCount + 1&, 0&, bmpInfo.bmiHeader.biWidth)

        ' ok, now to test whether or not we are good to go...
        ' if less than 2000 rectangles, function should have worked & if it didn't
        ' it wasn't due O/S restrictions -- failure

        If rtnRegion = 0& And rectCount > 2000& Then
            rtnRegion = c_CreateWin9xRegion(rectCount + 1&, 0&, bmpInfo.bmiHeader.biWidth)
            ' ^^ if rtnRegion is zero; windows could not create the region
        End If
    
    End If
    
CleanUp:
    
    Erase gpBytes  ' no longer needed; we can purge it now
    Erase rgnRects() ' no longer needed; we can purge it now
    
    If Err Then
        If rtnRegion <> 0& Then DeleteObject rtnRegion
'unRem next Debug line if the region is not being created.
'Debug.Print "Shaped Region failed. Windows could not create the region." & vbCrLf & "  Error: "; Err.Description
        Err.Clear
        ' return value for this function is now zero
    Else
        If hWndToApply <> 0& Then
            RegionFromBitmap = SetWindowRgn(hWndToApply, rtnRegion, True)
            ' ^^ if above API call fails, return value will be zero
        Else
            RegionFromBitmap = rtnRegion
            ' ^^ return handle to the shaped region
        End If
    End If

End Function

' /////////////////////////  CLASS ONLY FUNCTIONS \\\\\\\\\\\\\\\\\\\\
' all non-public class functions/routines are prefixed with c_ in their name

Private Function c_CreatePartialRegion(ByVal lIndex As Long, ByVal uIndex As Long, ByVal leftOffset As Long, ByVal Cx As Long, Optional ByVal xFrmPtr As Long) As Long

    ' Creates a region from a Rect() array and optionally stretches the region

    On Error Resume Next
    ' Note: Ideally, contiguous rows vertically of equal height & width should
    ' be combined into one larger row. However, thru trial & error I found
    ' that Windows does this for us and taking the extra time to do it ourselves
    ' is too cumbersome & slows down the results.
    
    ' the first 32 bytes of a region contain the header describing the region.
    ' Well, 32 bytes equates to 2 rectangles (16 bytes each), so I'll
    ' cheat a little & use rectangles to store the header
    With rgnRects(lIndex - 2&) ' bytes 0-15
        .Left = 32                      ' length of region header in bytes
        .Top = 1                        ' required cannot be anything else
        .Right = uIndex - lIndex + 1&   ' number of rectangles for the region
        .Bottom = .Right * 16&          ' byte size used by the rectangles;
    End With                            ' ^^ can be zero & Windows will calculate
    
    With rgnRects(lIndex - 1&) ' bytes 16-31 bounding rectangle identification
        .Left = leftOffset                  ' left
        .Top = rgnRects(lIndex).Top         ' top
        .Right = leftOffset + Cx            ' right
        .Bottom = rgnRects(uIndex).Bottom   ' bottom
    End With
    ' call function to create region from our byte (RECT) array
    c_CreatePartialRegion = ExtCreateRegion(ByVal xFrmPtr, (rgnRects(lIndex - 2&).Right + 2&) * 16&, rgnRects(lIndex - 2&))
    If Err Then Err.Clear

End Function

Private Function c_CreateWin9xRegion(ByVal rectCount As Long, ByVal leftOffset As Long, ByVal Cx As Long, Optional ByVal xFrmPtr As Long) As Long
' Pulled out of main routine 'cause now other routines share the same logic
' and we will simply share this part of the code

' Win98 has problems with regional rectangles over 4000
' So, we'll try again in case this is the prob with other systems too.
' We'll step it at 2000 at a time which is stil very fast

    Dim X As Long, Y As Long ' loop counters
    Dim win9xRgn As Long     ' partial region
    Dim rtnRegion As Long    ' combined region & return value of this function
    
    ' we start with 2 'cause first 2 RECTs are the header
    For X = 2& To rectCount Step 2000&
    
        If X + 2000& > rectCount Then
            Y = rectCount
        Else
            Y = X + 2000&
        End If
        
        ' attempt to create partial region
        win9xRgn = c_CreatePartialRegion(X, Y, leftOffset, Cx, xFrmPtr)
        
        If win9xRgn = 0& Then    ' failure
            ' clean up combined region if needed
            If rtnRegion Then DeleteObject rtnRegion
            rtnRegion = 0&
            Exit For ' abort
        Else
            If rtnRegion Then ' already started
                ' use combineRgn, but only every 2000th time
                CombineRgn rtnRegion, rtnRegion, win9xRgn, RGN_OR
                DeleteObject win9xRgn
            Else    ' first time thru
                rtnRegion = win9xRgn
            End If
        End If
    Next
    ' done; return result
    c_CreateWin9xRegion = rtnRegion

End Function

Private Sub c_ScanSection(ByVal Y As Long, ByVal Cx As Long, ByVal Cx2 As Long, ByVal Cx3 As Long, _
        ByVal Cy As Long, ByVal transColor As Long, ByVal returnAntiRegion As Boolean, ByRef rectCount As Long)

' function added as an extension of the main routine; therefore, some of the
' user passed variables are also passed here too

' This function will scan a specific number of bitmap lines and can skip over
' a continguous section of pixels identified by the exclusion rectangle.
' Note the complexity below. The exlusion rectangle may not be aligned with
' any of the edges of the bitmap so we may have to process pixels left, right,
' above and below the exclusion rectangle.

' See RegionFromBitmap additional remarks about the exclusion rectangle

    Dim scanX As Long, scanY As Long, X As Long ' simple loop variables
    Dim tgtColor As Long                ' a DIB pixel color (BGR format)
    Dim rStart As Long                  ' rectangle tracking started
    Dim bExludeDone As Boolean          ' exclusion rectangle processed horizontally
    Dim endX As Long                    ' last byte on line to scan
    Dim maxRectCount As Long            ' UBound(rgnRects)
    
    ' reset flag
    rStart = -1&
    endX = Cx
    maxRectCount = UBound(rgnRects)
    
    ' begin pixel by pixel comparisons
    For scanY = Y To Cy - 1&
    
        For scanX = X To endX - 1&
            ' my hack continued: we already saved a long as BGR, now
            ' get the current DIB pixel into a long (BGR also) & compare
            CopyMemory tgtColor, gpBytes(scanX * 3&, scanY), &H3
            
            ' test to see if next pixel is a target color
            If (transColor = tgtColor) Xor returnAntiRegion Then
            
                If rStart > -1& Then ' we're currently tracking a rectangle, so let's close it
                    ' see if array needs to be resized
                   If rectCount + 1& = maxRectCount Then
                        maxRectCount = UBound(rgnRects) + Cx3
                        ReDim Preserve rgnRects(0 To maxRectCount)
                    End If
                    
                    ' add the rectangle to our array
                    SetRect rgnRects(rectCount + 2&), rStart, scanY, scanX, scanY + 1&
                    rStart = -1& ' reset flag
                    rectCount = rectCount + 1&     ' keep track of nr in use
                End If
            
            Else
                ' not a target color
                If rStart = -1& Then rStart = scanX ' set start point
            
            End If
        Next scanX
        
        If rStart > -1& Then
            ' got to end of section without hitting another transparent pixel
            ' but we're tracking so we'll close rectangle now
           
            ' see if array needs to be resized
            If rectCount + 1& = maxRectCount Then
                 maxRectCount = UBound(rgnRects) + Cx3
                 ReDim Preserve rgnRects(0 To maxRectCount)
             End If
            ' add the rectangle to our array
            SetRect rgnRects(rectCount + 2&), rStart, scanY, scanX, scanY + 1&
            rStart = -1& ' reset flag
            rectCount = rectCount + 1&     ' keep track of nr in use
        End If
        
        ' exclusion rectangle checks here
        If bExludeDone Then
            ' when the left & right side of exclusion rect finished, reset variables
            bExludeDone = False
            X = 0&
            endX = Cx
        Else
            ' see if we have an exclusion rectangle being processed
            If Cx2 > 0& Then             ' exclusion rectangle passed?
                If Cx2 <> Cx3 Then      ' exclusion rectangle was passed
                    
                    ' add the excluded portion of the bitmap to our region
                    SetRect rgnRects(rectCount + 2&), Cx, scanY, Cx2 + 1&, scanY + 1&
                    rectCount = rectCount + 1&
                    ' set the next column of pixels to be checked (right side of rectangle)
                    X = Cx2
                    ' set the last column to be checked (left edge of bitmap)
                    endX = Cx3
                    ' decrement the loop variable so it will run same row again
                    scanY = scanY - 1&
                    ' toggle flag so X & endX can be restored after row is processed
                    bExludeDone = True
                End If
            End If
        End If
        
    Next scanY

End Sub
