Region class

A Region describes an area of a drawing surface, of any shape, made from rectangles and paths and combined with the operations of set theory.

Regions are used to clip drawing, with the SetClip methods of Graphics, to fill an area with FillRegion, and to test whether a point or a rectangle is inside an area. A region is stored in world coordinates, so it scales and moves with the transformation of the graphics object it is used with. A Windows GDI region is stored in device coordinates.

A new region is infinite: it covers the whole plane. MakeEmpty turns it into an area with no points, and the combining methods Intersect, Union, Xor, Exclude and Complement change it by a rectangle, a path or another region.

The methods that describe a region in terms of a drawing surface, such as Bounds, IsEmpty and IsVisible, take a Graphics argument g for its transformation. It can be any graphics object. For Bounds, IsEmpty, IsInfinite, IsEqualTo and GetHRGN it must be a real object: with Nothing the call records InvalidParameter and returns an empty result. The IsVisible methods accept Nothing, which means no transformation.

Most members return a GpStatus, which is Ok when the call worked. A function or property that fails records its status in LastResult, as every object of the package does. See GdiPlusBase. Members that take coordinates have a floating-point and an integer form. The floating-point form takes Single values or a GpRectF or GpPointF. The integer form takes GpRect or GpPoint, or is the method with the extra letter I, such as TranslateI.

Creating

Syntax: region = Region( [ shape ] )

Syntax: region = New Region( [ shape ] )

The function Region and New Region are the same thing, and take the same arguments.

shape
optional What the region covers. Without it the region is infinite. It can be a GpRectF or a GpRect, the rectangle to cover; a GraphicsPath, whose inside the region covers, as its fill mode decides; or an HRGN, a Windows GDI region handle, passed as a LongPtr, which the region copies.

Syntax: region = Region( regionData, size )

regionData
required The first byte of a Byte array that holds region data from GetData.
size
required The number of bytes of data.

Creates a region from data that GetData wrote earlier, which can be stored and used in another program.

Syntax: region = FromHRGN( hRgn )

Creates a region from a GDI region handle. It is the same as Region( hRgn ). The region keeps a copy: the program still owns the handle and deletes it with the GDI function DeleteObject.

Dim user As GdiPlusUser = GdiPlusUser()
Dim bmp As Bitmap = Bitmap(10!, 10!)
Dim g As Graphics = Graphics(bmp)

Dim all As Region = Region()
Debug.Print all.IsInfinite(g) & " " & all.IsEmpty(g)       ' True False
Dim box As Region = Region(GpRectF(10, 20, 30, 40))
Debug.Print box.IsInfinite(g) & " " & box.IsVisible(15, 25, g)    ' False True

Clone

Syntax: region.Clone As Region

Returns a new region that covers the same area. A change to one region does not change the other.

Combining

Intersect

Syntax: region.Intersect( shape ) As GpStatus

shape
required A GpRectF, a GpRect, a GraphicsPath or a Region.

Changes the region to the part of it that is also inside shape.

Union

Syntax: region.Union( shape ) As GpStatus

Changes the region to everything that is inside the region, shape or both.

Xor

Syntax: region.Xor( shape ) As GpStatus

Changes the region to the parts that are inside the region or shape but not both.

Exclude

Syntax: region.Exclude( shape ) As GpStatus

Changes the region to the part of it that is not inside shape.

Complement

Syntax: region.Complement( shape ) As GpStatus

Changes the region to the part of shape that is not inside the region. It is Exclude the other way round.

The arguments of the five methods are the same. This example starts each time from the square from 0, 0 to 50, 50 and combines it with the square from 25, 0 to 75, 50, then reads the bounds of the result:

Dim user As GdiPlusUser = GdiPlusUser()
Dim bmp As Bitmap = Bitmap(10!, 10!)
Dim g As Graphics = Graphics(bmp)
Dim other As GpRectF = GpRectF(25, 0, 50, 50)
Dim b As GpRectF

Dim r As Region = Region(GpRectF(0, 0, 50, 50))
r.Intersect other
b = r.Bounds(g)
Debug.Print "intersect " & b.X & " " & b.Width        ' intersect 25 25

Set r = Region(GpRectF(0, 0, 50, 50))
r.Union other
b = r.Bounds(g)
Debug.Print "union " & b.X & " " & b.Width            ' union 0 75

Set r = Region(GpRectF(0, 0, 50, 50))
r.Exclude other
b = r.Bounds(g)
Debug.Print "exclude " & b.X & " " & b.Width          ' exclude 0 25

Set r = Region(GpRectF(0, 0, 50, 50))
r.Complement other
b = r.Bounds(g)
Debug.Print "complement " & b.X & " " & b.Width       ' complement 50 25

Set r = Region(GpRectF(0, 0, 50, 50))
r.Xor other
Debug.Print "xor " & r.IsVisible(10, 10, g) & " " & r.IsVisible(30, 10, g) & " " & r.IsVisible(60, 10, g)    ' xor True False True

State

MakeInfinite

Syntax: region.MakeInfinite As GpStatus

Changes the region to cover the whole plane.

MakeEmpty

Syntax: region.MakeEmpty As GpStatus

Changes the region to cover nothing.

IsEmpty and IsInfinite

Syntax: region.IsEmpty( g ) As Boolean

Syntax: region.IsInfinite( g ) As Boolean

True when the region covers nothing, or the whole plane.

IsEqualTo

Syntax: region.IsEqualTo( other, g ) As Boolean

True when the two regions cover the same area.

Dim user As GdiPlusUser = GdiPlusUser()
Dim bmp As Bitmap = Bitmap(10!, 10!)
Dim g As Graphics = Graphics(bmp)
Dim r As Region = Region()
r.MakeEmpty
Debug.Print r.IsEmpty(g) & " " & r.IsInfinite(g)           ' True False
r.MakeInfinite
Debug.Print r.IsEmpty(g) & " " & r.IsInfinite(g)           ' False True
Dim a As Region = Region(GpRectF(1, 2, 3, 4))
Debug.Print a.IsEqualTo(a.Clone, g) & " " & a.IsEqualTo(r, g)      ' True False

Bounds

Bounds and BoundsI

Syntax: region.Bounds( g ) As GpRectF

Syntax: region.BoundsI( g ) As GpRect

Returns a rectangle that holds the whole region, in the coordinates of the graphics object g. Bounds returns a GpRectF, BoundsI a GpRect with whole numbers.

Moving and transforming

Translate and TranslateI

Syntax: region.Translate( dx, dy ) As GpStatus

Syntax: region.Translate( offset ) As GpStatus

Syntax: region.TranslateI( dx, dy ) As GpStatus

dx, dy
required The distances to move by along each axis, Single for Translate and Long for TranslateI.
offset
required A GpPointF or a GpPoint.

Moves the region.

Transform

Syntax: region.Transform( matrix ) As GpStatus

matrix
required A Matrix.

Multiplies each point of the region by the matrix.

Dim user As GdiPlusUser = GdiPlusUser()
Dim bmp As Bitmap = Bitmap(10!, 10!)
Dim g As Graphics = Graphics(bmp)
Dim r As Region = Region(GpRectF(0, 0, 50, 50))
r.Translate 5, 6
Dim b As GpRectF = r.Bounds(g)
Debug.Print b.X & " " & b.Y & " " & b.Width        ' 5 6 50
Dim m As Matrix = Matrix()
m.Scale 2, 2
r.Transform m
b = r.Bounds(g)
Debug.Print b.X & " " & b.Y & " " & b.Width        ' 10 12 100

Hit testing

IsVisible

Syntax: region.IsVisible( x, y, g ) As Boolean

Syntax: region.IsVisible( point, g ) As Boolean

Syntax: region.IsVisible( x, y, width, height, g ) As Boolean

Syntax: region.IsVisible( rect, g ) As Boolean

Syntax: region.IsVisibleI( x, y, g ) As Boolean

Syntax: region.IsVisibleI( x, y, width, height, g ) As Boolean

With a point, True when the point is inside the region. With a rectangle, True when the rectangle and the region have a point in common. g can be Nothing.

Dim user As GdiPlusUser = GdiPlusUser()
Dim r As Region = Region(GpRectF(10, 10, 20, 20))
Debug.Print r.IsVisible(15, 15, Nothing) & " " & r.IsVisible(GpPointF(5, 5), Nothing)    ' True False
Debug.Print r.IsVisible(GpRectF(0, 0, 12, 12), Nothing) & " " & r.IsVisible(GpRectF(0, 0, 5, 5), Nothing)    ' True False
Debug.Print r.IsVisibleI(15, 15, Nothing)          ' True

Scans

The scans are a set of rectangles that approximate the region. A rectangular region has one; a region with curved edges has many thin ones.

GetRegionScansCount

Syntax: region.GetRegionScansCount( matrix ) As Long

The number of rectangles. The number can depend on the matrix, a Matrix, which is applied to the region first; it is normally the world-to-device transformation of a graphics object, or an identity matrix.

GetRegionScans and GetRegionScansI

Syntax: region.GetRegionScans( matrix ) As GpRectF()

Syntax: region.GetRegionScansI( matrix ) As GpRect()

Syntax: region.GetRegionScans( matrix, rects, count ) As GpStatus

rects
required The first element of an array of GpRectF or GpRect that has room for all the rectangles. Call GetRegionScansCount first to find out how many.
count
required A Long variable that receives the number of rectangles written.

The array forms return the rectangles in an array with indexes from 1. The array is empty when the region has no rectangles. The last form writes into an array that the program owns.

Dim user As GdiPlusUser = GdiPlusUser()
Dim r As Region = Region(GpRectF(5, 6, 10, 20))
Dim m As Matrix = Matrix()
Debug.Print r.GetRegionScansCount(m)               ' 1
Dim sc() As GpRectF = r.GetRegionScans(m)
Debug.Print sc(1).X & " " & sc(1).Y & " " & sc(1).Width & " " & sc(1).Height     ' 5 6 10 20

Dim buf(1 To 4) As GpRectF
Dim n As Long = 4
Debug.Print r.GetRegionScans(m, buf(1), n) & " " & n      ' 0 1

Saving a region

GetDataSize

Syntax: region.GetDataSize As Long

The number of bytes that GetData writes.

GetData

Syntax: region.GetData( buffer, bufferSize, [ sizeFilled ] ) As GpStatus

buffer
required The first byte of a Byte array with at least bufferSize bytes.
bufferSize
required The size of the array, in bytes. It should be at least GetDataSize.
sizeFilled
optional A Long variable that receives the number of bytes written.

Writes a description of the region into the array. A program creates a region from the data again with the constructor that takes data.

Dim user As GdiPlusUser = GdiPlusUser()
Dim bmp As Bitmap = Bitmap(10!, 10!)
Dim g As Graphics = Graphics(bmp)
Dim r As Region = Region(GpRectF(5, 6, 10, 20))
Dim size As Long = r.GetDataSize
Dim data() As Byte
ReDim data(0 To size - 1)
Dim filled As Long
r.GetData data(0), size, filled
Debug.Print (filled = size)                        ' True

Dim copy As Region = Region(data(0), filled)
Dim b As GpRectF = copy.Bounds(g)
Debug.Print b.X & " " & b.Y & " " & b.Width & " " & b.Height      ' 5 6 10 20

Windows regions

GetHRGN

Syntax: region.GetHRGN( g ) As LongPtr

Creates a Windows GDI region with the same area, in device coordinates, and returns its handle. The program owns the handle and deletes it with the GDI function DeleteObject. The handle is 0 if the call fails.

The two directions use the GDI functions CreateRectRgn, GetRgnBox and DeleteObject, which the example declares:

Declare PtrSafe Function CreateRectRgn Lib "gdi32" (ByVal x1 As Long, ByVal y1 As Long, ByVal x2 As Long, ByVal y2 As Long) As LongPtr
Declare PtrSafe Function DeleteObject Lib "gdi32" (ByVal h As LongPtr) As Long
Declare PtrSafe Function GetRgnBox Lib "gdi32" (ByVal h As LongPtr, ByRef r As Long) As Long
Dim user As GdiPlusUser = GdiPlusUser()
Dim bmp As Bitmap = Bitmap(100!, 100!)
Dim g As Graphics = Graphics(bmp)

Dim h As LongPtr = CreateRectRgn(10, 20, 40, 60)
Dim r As Region = FromHRGN(h)
DeleteObject h                                     ' the region keeps a copy
Dim b As GpRectF = r.Bounds(g)
Debug.Print b.X & " " & b.Y & " " & b.Width & " " & b.Height    ' 10 20 30 40

Dim h2 As LongPtr = r.GetHRGN(g)
Dim box(0 To 3) As Long                            ' left, top, right, bottom
GetRgnBox h2, box(0)
Debug.Print box(0) & " " & box(1) & " " & box(2) & " " & box(3)    ' 10 20 40 60
DeleteObject h2

Example

This example builds a ring from two ellipses, draws it, and tests two points. It then clips the drawing with the ring.

Dim user As GdiPlusUser = GdiPlusUser()
Dim bmp As Bitmap = Bitmap(40!, 40!)
Dim g As Graphics = Graphics(bmp)
g.Clear Color(255, 255, 255, 255)

Dim outer As GraphicsPath = GraphicsPath()
outer.AddEllipse 2, 2, 36, 36
Dim inner As GraphicsPath = GraphicsPath()
inner.AddEllipse 10, 10, 20, 20
Dim ring As Region = Region(outer)
ring.Exclude inner

Dim blue As SolidBrush = New SolidBrush(Color(255, 0, 0, 255))
g.FillRegion blue, ring
Debug.Print Hex$(bmp.Pixel(20, 5).R) & " " & Hex$(bmp.Pixel(20, 5).B)       ' 0 FF
Debug.Print Hex$(bmp.Pixel(20, 20).R) & " " & Hex$(bmp.Pixel(20, 20).B)     ' FF FF

g.SetClip ring
g.Clear Color(255, 255, 0, 0)
Debug.Print Hex$(bmp.Pixel(20, 5).G) & " " & Hex$(bmp.Pixel(20, 20).G)      ' 0 FF
Debug.Print ring.IsVisible(20, 5, g) & " " & ring.IsVisible(20, 20, g)      ' True False

See Also

  • GraphicsPath class – a path can become a region
  • Matrix class – the transformation applied by Transform
  • Graphics class – FillRegion and SetClip use a region
  • GpCombineMode enumeration – the combining operations as the SetClip methods name them
  • Region class – Microsoft’s documentation of the GDI+ class