Copyright (c) 2012-2016 Hyperion Entertainment and contributors.

Graphics Regions

From AmigaOS Documentation Wiki
Jump to: navigation, search

Regions

Regions allow the application to install clipping rectangles into layers. A clipping rectangle is a rectangular area into which the graphics routines will draw. All drawing that would fall outside of that rectangular area is clipped (not rendered).

User clipping regions are linked lists of clipping rectangles created by an application program through the Graphics Library routines described below. By combining together various clipping rectangles, any arbitrary clipping shape can be created. Once the region is set up, you use the Layers Library call InstallClipRegion() to make the clipping region active in a layer.

Regions are safe to use with layers created by Intuition (i.e. windows).

The application can selectively update a custom-shaped part of a layer without disturbing any of the other layers that might be present.

Never modify the DamageList of a layer directly
Use the routine InstallClipRegion() to add clipping to the layer. The regions installed by InstallClipRegion() are independent of the layer's DamageList and use of user clipping regions will not interfere with optimized window refreshing.
Do not modify a region after it has been added
After a region has been added with InstallClipRegion(), the program may not modify it until the region has been removed with another call to InstallClipRegion().

Creating and Deleting Regions

You allocate a Region data structure with the NewRegion() call.

struct Region *NewRegion( VOID );

The NewRegion() function allocates and initializes a Region structure that has no drawable areas defined in it. If the application draws through a new region, nothing will be drawn as the region is empty. The application must add rectangles to the region before any graphics will appear.

Use DisposeRegion() to free the Region structure when you are done with it.

VOID DisposeRegion( struct Region *region );

DisposeRegion() returns all memory associated with a region to the system and deallocates all rectangles that have been linked to it.

Don't forget to free your rectangles
All of the functions that add rectangles to the region make copies of the rectangles. If the program allocates a rectangle, then adds it to a region, it still must deallocate the rectangle. The call to DisposeRegion() will not deallocate rectangles explicitly allocated by the application.

Installing Regions

Use the function InstallClipRegion() to install the region.

struct Region *InstallClipRegion( struct Layer *layer, struct Region *region );

This installs a transparent clipping region to a layer. All subsequent graphics calls will be clipped to this region. The region must be removed with a second call to InstallClipRegion() before removing the layer.

/*
** Sample installation and removal of a clipping region
*/
register struct Region     *new_region ;
register struct Region     *old_region ;
 
/* If the application owns the layer and has not installed a region,
** old_region will return NULL here.
*/
old_region = ILayers->InstallClipRegion(win->WLayer, new_region);
 
/* draw into the layer or window */
 
if (NULL != (old_region = ILayers->InstallClipRegion(win->WLayer, old_region)))
    {
    /* throw the used region away.  This region could be saved and
    ** used again later, if desired by the application.
    */
    ILayers->DisposeRegion(new_region) ;
    }
A warning about InstallClipRegion()
The program must not call InstallClipRegion() inside of a Begin/EndRefresh() or Begin/EndUpdate() pair. The following code segment shows how to modify the user clipping region when using these calls. See the Autodoc for BeginRefresh() for more details.
struct Region     *new_region ;
struct Region     *old_region ;
 
/* you have to have already setup the new_region and old_region */
 
IIntuition->BeginRefresh(window);
/* draw through the damage list */
/* into the layer or window */
IIntuition->EndRefresh(window, FALSE);              /* keep the damage list */
 
old_region = ILayers->InstallClipRegion(win->WLayer, new_region);
 
IIntuition->BeginRefresh(window);
/* draw through the damage list and the new_region */
/* into the layer or window */
IIntuition->EndRefresh(window, FALSE);              /* keep the damage list */
 
/* put back the old region */
new_region = ILayers->InstallClipRegion(win->WLayer, old_region);
 
IIntuition->BeginRefresh(window);
IIntuition->EndRefresh(window, TRUE);               /* remove the damage list */
 
old_region = ILayers->InstallClipRegion(win->WLayer, new_region);
 
IIntuition->BeginRefresh(window);
/* draw through the new_region only into the layer or window */
IIntuition->EndRefresh(window, FALSE);
 
/* finally get rid of the new region, old_region still installed */
if (NULL != (new_region = ILayers->InstallClipRegion(win->WLayer, old_region)))
    ILayers->DisposeRegion(new_region) ;

Changing a Region

Regions may be modified by performing logical operations with rectangles, or with other regions.

Reuse your rectangles
In all of the rectangle and region routines the clipping rectangle is copied into the region. This means that a single clipping rectangle (Rectangle structure) may be used many times by simply changing the x and y values. The application need not create a new instance of the Rectangle structure for each rectangle added to a region.

For instance:

extern struct Region *RowRegion;  /* created elsewhere */
 
int16 ktr;
struct Rectangle rect;
 
for (ktr = 1; ktr < 6; ktr++)
    {
    rect.MinX = 50;
    rect.MaxX = 315;
    rect.MinY = (ktr * 10) - 5;
    rect.MaxY = (ktr * 10);
 
    if (!IGraphics->OrRectRegion(RowRegion, &rect))
        clean_exit(RETURN_WARN);
    }

Rectangles and Regions

There are four Graphics Library functions for changing a region through logical operations with a rectangle.

BOOL OrRectRegion   ( struct Region *region, struct Rectangle *rectangle );
VOID AndRectRegion  ( struct Region *region, struct Rectangle *rectangle );
BOOL XorRectRegion  ( struct Region *region, struct Rectangle *rectangle );
BOOL ClearRectRegion( struct Region *region, struct Rectangle *rectangle );

OrRectRegion() modifies a region structure by OR'ing a clipping rectangle into the region. When the application draws through this region (assuming that the region was originally empty), only the pixels within the clipping rectangle will be affected. If the region already has drawable areas, they will still exist, this rectangle is added to the drawable area.

AndRectRegion() modifies the region structure by AND'ing a clipping rectangle into the region. Only those pixels that were already drawable and within the rectangle will remain drawable, any that are outside of it will be clipped in future.

XorRectRegion() applies the rectangle to the region in an exclusive-or mode. Within the given rectangle, any areas that were drawable become clipped, any areas that were clipped become drawable. Areas outside of the rectangle are not affected.

ClearRectRegion() clears the rectangle from the region. Within the given rectangle, any areas that were drawable become clipped. Areas outside of the rectangle are not affected.

Regions and Regions

As with rectangles and regions, there are four functions for combining regions with regions:

BOOL AndRegionRegion( struct Region *srcRegion, struct Region *destRegion );
BOOL OrRegionRegion ( struct Region *srcRegion, struct Region *destRegion );
BOOL XorRegionRegion( struct Region *srcRegion, struct Region *destRegion );
VOID ClearRegion    ( struct Region *region );

AndRegionRegion() performs a logical AND operation on the two regions, leaving the result in the second region. The operation leaves drawable areas wherever the regions drawable areas overlap. That is, where there are drawable areas in both region 1 AND region 2, there will be drawable areas left in the result region.

OrRegionRegion() performs a logical OR operation on the two regions, leaving the result in the second region. The operation leaves drawable areas wherever there are drawable areas in either region. That is, where there are drawable areas in either region 1 OR region 2, there will be drawable areas left in the result region.

XorRegionRegion() performs a logical exclusive-or operation on the two regions, leaving the result in the second region. The operation leaves drawable areas wherever there are drawable areas in either region but not both. That is, where there are drawable areas in either region 1 OR region 2, there will be drawable areas left in the result region. But where there are drawable areas in both region 1 AND region 2, there will not be drawable areas left in the result region.

ClearRegion() puts the region back to the same state it was in when the region was created with NewRegion(), that is, no areas are drawable.

Regions Example

The following example shows the use of the Layers Library call InstallClipRegion(), as well as simple use of the Graphics Library regions functions.

/* clipping.c */
 
/* Force use of new variable names to help prevent errors */
#define INTUI_V36_NAMES_ONLY
 
#include <exec/types.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <graphics/displayinfo.h>
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/layers.h>
 
#define MY_WIN_WIDTH  (300)
#define MY_WIN_HEIGHT (100)
 
struct IntuitionIFace *IIntuition;
struct GraphicsIFace *IGraphics;
struct LayersIFace *ILayers;
 
/*
** unclipWindow()
**
** Used to remove a clipping region installed by clipWindow() or
** clipWindowToBorders(), disposing of the installed region and
** reinstalling the region removed.
*/
void unclipWindow(struct Window *win)
{
struct Region     *old_region;
 
/* Remove any old region by installing a NULL region,
** then dispose of the old region if one was installed.
*/
if (NULL != (old_region = ILayers->InstallClipRegion(win->WLayer, NULL)))
    IGraphics->DisposeRegion(old_region);
}
 
/*
** clipWindow()
** Clip a window to a specified rectangle (given by upper left and
** lower right corner.)  the removed region is returned so that it
** may be re-installed later.
*/
struct Region *clipWindow(struct Window *win,
    LONG minX, LONG minY, LONG maxX, LONG maxY)
{
struct Region    *new_region;
struct Rectangle  my_rectangle;
 
/* set up the limits for the clip */
my_rectangle.MinX = minX;
my_rectangle.MinY = minY;
my_rectangle.MaxX = maxX;
my_rectangle.MaxY = maxY;
 
/* get a new region and OR in the limits. */
if (NULL != (new_region = IGraphics->NewRegion()))
    {
    if (FALSE == IGraphics->OrRectRegion(new_region, &my_rectangle))
        {
        IGraphics->DisposeRegion(new_region);
        new_region = NULL;
        }
    }
 
/* Install the new region, and return any existing region.
** If the above allocation and region processing failed, then
** new_region will be NULL and no clip region will be installed.
*/
return(ILayers->InstallClipRegion(win->WLayer, new_region));
}
 
/*
** clipWindowToBorders()
** clip a window to its borders.
** The removed region is returned so that it may be re-installed later.
*/
struct Region *clipWindowToBorders(struct Window *win)
{
return(clipWindow(win, win->BorderLeft, win->BorderTop,
    win->Width - win->BorderRight - 1, win->Height - win->BorderBottom - 1));
}
 
/*
** Wait for the user to select the close gadget.
*/
VOID wait_for_close(struct Window *win)
{
struct IntuiMessage *msg;
BOOL done;
 
done = FALSE;
 
while (FALSE == done)
    {
    /* we only have one signal bit, so we do not have to check which
    ** bit broke the Wait().
    */
    IExec->Wait(1L << win->UserPort->mp_SigBit);
 
    while ( (FALSE == done) &&
            (NULL != (msg = (struct IntuiMessage *)IExec->GetMsg(win->UserPort))))
        {
        /* use a switch statement if looking for multiple event types */
        if (msg->Class == IDCMP_CLOSEWINDOW)
            done = TRUE;
 
        IExec->ReplyMsg((struct Message *)msg);
        }
    }
}
 
/*
** Simple routine to blast all bits in a window with color three to show
** where the window is clipped.  After a delay, flush back to color zero
** and refresh the window borders.
*/
VOID draw_in_window(struct Window *win, UBYTE *message)
{
IDOS->Printf("%s...", message);
IGraphics->SetRast(win->RPort, 3);
IDOS->Delay(200);
IGraphics->SetRast(win->RPort, 0);
IIntuition->RefreshWindowFrame(win);
IDOS->Printf("done\n");
}
 
 
/*
** Show drawing into an unclipped window, a window clipped to the
** borders and a window clipped to a random rectangle.  It is possible
** to clip more complex shapes by AND'ing, OR'ing and exclusive-OR'ing
** regions and rectangles to build a user clip region.
**
** This example assumes that old regions are not going to be re-used,
** so it simply throws them away.
*/
VOID clip_test(struct Window *win)
{
struct Region    *old_region;
 
draw_in_window(win,"Window with no clipping");
 
/* if the application has never installed a user clip region,
** then old_region will be NULL here.  Otherwise, delete the
** old region (you could save it and re-install it later...)
*/
if (NULL != (old_region = clipWindowToBorders(win)))
    IGraphics->DisposeRegion(old_region);
draw_in_window(win,"Window clipped to window borders");
unclipWindow(win);
 
/* here we know old_region will be NULL, as that is what we
** installed with unclipWindow()...
*/
if (NULL != (old_region = clipWindow(win,20,20,100,50)))
    IGraphics->DisposeRegion(old_region);
draw_in_window(win,"Window clipped from (20,20) to (100,50)");
unclipWindow(win);
 
wait_for_close(win);
}
 
 
 
/*
** Open and close resources, call the test routine when ready.
*/
int main(int argc, char **argv)
{
  struct Window *win;
 
  struct Library *IntuitionBase = IExec->OpenLibrary("intuition.library", 50);
  IIntuition = (struct IntuitionIFace*)IExec->GetInterface(IntuitionBase, "main", 1, NULL);
 
  struct Library *GfxBase = IExec->OpenLibrary("graphics.library", 50);
  IGraphics = (struct GraphicsIFace*)IExec->GetInterface(GfxBase, "main", 1, NULL);
 
  struct Library *LayersBase = IExec->OpenLibrary("layers.library", 50);
  ILayers = (struct LayersIFace*)IExec->GetInterface(LayersBase, "main", 1, NULL);
 
  if (IIntuition != NULL && IGraphics != NULL && ILayers != NULL)
  {
    if (NULL != (win = IIntuition->OpenWindowTags(NULL,
                             WA_Width,       MY_WIN_WIDTH,
                             WA_Height,      MY_WIN_HEIGHT,
                             WA_IDCMP,       IDCMP_CLOSEWINDOW,
                             WA_CloseGadget, TRUE,
                             WA_DragBar,     TRUE,
                             WA_Activate,    TRUE,
                             TAG_END)))
    {
      clip_test(win);
 
      IIntuition->CloseWindow(win);
    }
  }
 
  IExec->DropInterface((struct Interface*)ILayers);
  IExec->CloseLibrary(LayersBase);
 
  IExec->DropInterface((struct Interface*)IGraphics);
  IExec->CloseLibrary(GfxBase);
 
  IExec->DropInterface((struct Interface*)IIntuition);
  IExec->CloseLibrary(IntuitionBase);
 
  return 0;
}

Function Reference

The following are brief descriptions of the Graphics Library functions related to regions. See the SDK for details on each function call.

Routine Description
LockLayerRom() Same as LockLayer(), from the Layers Library.
UnlockLayerRom() Release LockLayerRom() lock.
AttemptLockLayerRom() Lock layer only if it is immediately available.
NewRegion() Create a new, empty region.
DisposeRegion() Dispose of an existing region and its rectangles.
AndRectRegion() And a rectangle into a region.
OrRectRegion() Or a rectangle into a region.
XorRectRegion() Exclusive-or a rectangle into a region.
ClearRectRegion() Clear a rectangular portion of a region.
AndRegionRegion() And two regions together.
OrRegionRegion() Or two regions together.
XorRegionRegion() Exclusive-or two regions together.
ClearRegion() Clear a region.