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

Intuition Borders

From AmigaOS Documentation Wiki
Jump to: navigation, search

Creating Borders

This data type is called a Border since it was originally used to create border lines around display objects. It is actually a general purpose structure for drawing connected lines between any series of points.

A Border is easier to use than an Image structure. Only the following need be specified to define a border:

  • An internal position component which is used in determining the final position of the border.
  • A set of coordinate pairs for each vertex.
  • A color for the lines.
  • One of several drawing modes.

Border Structure Definition

To use a border, the application must create one or more instances of the Border structure. Here is the specification:

struct Border
    {
    WORD LeftEdge, TopEdge;
    UBYTE FrontPen, BackPen;
    UBYTE DrawMode;
    BYTE Count;
    WORD *XY;
    struct Border *NextBorder;
    };

Here is a brief description of the fields of the Border structure.

LeftEdge, TopEdge
These fields are used to determine the position of the Border relative to its base position (the base position is the upper left corner for requesters, menus, or gadgets and is specified in the call to DrawBorder() for windows and screens).
FrontPen, BackPen
These fields contain color registers numbers. FrontPen is the color used to draw the lines. BackPen is currently unused.
DrawMode
Set the DrawMode field to one of the following:
JAM1 Use FrontPen to draw the line.
COMPLEMENT Change the pixels within the lines to their complement color.
Count
Specify the number of data points used in this border. Each data point is described by two words of data in the XY array.
XY
A pointer to an array of coordinate pairs, one pair for each point. These coordinates are measured relative to the position of the border.
NextBorder
This field is a pointer to another instance of a Border structure. Set this field to NULL if this is the last Border structure in the linked list.

Directly Drawing the Borders

Borders may be directly drawn by the application by calling the function DrawBorder().

VOID DrawBorder( struct RastPort *rp, struct Border *border,
                 LONG leftOffset, LONG topOffset );

The rp argument is a pointer to the RastPort into which the border should be drawn. This rastport may come from a Window or Screen structure.

The border argument is a pointer to a list of Border structures which are to be rendered. The list may contain a single Border structure.

The leftOffset and topOffset arguments are the external component, or base position, for this list of Borders. The LeftEdge and TopEdge values of each Border structure are added to these to determine the Border position.

Borders may also be indirectly drawn by attaching them to gadgets, menus or requesters.

Border Example

The following example draws a double border using two pens to create a shadow effect. The border is drawn in two positions to show the flexibility in positioning borders, note that it could also be attached to a menu, gadget or requester.

/*
** shadowborder.c - program to show the use of an Intuition Border.
*/
#include <exec/types.h>
#include <intuition/intuition.h>
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
 
struct Library *IntuitionBase = NULL;
 
#define MYBORDER_LEFT   (0)
#define MYBORDER_TOP    (0)
 
/* This is the border data. */
int16 myBorderData[] =
{
  0,0, 50,0, 50,30, 0,30, 0,0,
};
 
 
/*
** main routine. Open required library and window and draw the images.
** This routine opens a very simple window with no IDCMP.  See the
** articles on "Windows" and "Input and Output Methods" for more info.
** Free all resources when done.
*/
int main()
{
  struct Screen   *screen;
  struct DrawInfo *drawinfo;
  struct Window   *win;
  struct Border    shineBorder;
  struct Border    shadowBorder;
 
  uint32 mySHADOWPEN = 1;  /* set default values for pens */
  uint32 mySHINEPEN  = 2;  /* in case can't get info...   */
 
  struct Library *IntuitionBase = IExec->OpenLibrary("intuition.library", 50);
  IIntuition = (struct IntuitionIFace*)IExec->GetInterface(IntuitionBase, "main", 1, NULL);
 
  if (IIntuition != NULL)
  {
    if (screen = IIntuition->LockPubScreen(NULL))
    {
      if (drawinfo = IIntuition->GetScreenDrawInfo(screen))
      {
        /* Get a copy of the correct pens for the screen.
        ** This is very important in case the user or the
        ** application has the pens set in a unusual way.
        */
        mySHADOWPEN = drawinfo->dri_Pens[SHADOWPEN];
        mySHINEPEN  = drawinfo->dri_Pens[SHINEPEN];
 
        IIntuition->FreeScreenDrawInfo(screen,drawinfo);
      }
 
      IIntuition->UnlockPubScreen(NULL,screen);
    }
 
    /* open a simple window on the workbench screen for displaying
    ** a border.  An application would probably never use such a
    ** window, but it is useful for demonstrating graphics...
    */
    if (win = IIntuition->OpenWindowTags(NULL,
                        WA_PubScreen,  screen,
                        WA_RMBTrap,      TRUE,
                        TAG_END))
    {
      /* set information specific to the shadow component of the border */
      shadowBorder.LeftEdge   = MYBORDER_LEFT + 1;
      shadowBorder.TopEdge    = MYBORDER_TOP + 1;
      shadowBorder.FrontPen   = mySHADOWPEN;
      shadowBorder.NextBorder = &shineBorder;
 
      /* set information specific to the shine component of the border */
      shineBorder.LeftEdge    = MYBORDER_LEFT;
      shineBorder.TopEdge     = MYBORDER_TOP;
      shineBorder.FrontPen    = mySHINEPEN;
      shineBorder.NextBorder  = NULL;
 
      /* the following attributes are the same for both borders. */
      shadowBorder.BackPen    = shineBorder.BackPen   = 0;
      shadowBorder.DrawMode   = shineBorder.DrawMode  = JAM1;
      shadowBorder.Count      = shineBorder.Count     = 5;
      shadowBorder.XY         = shineBorder.XY        = myBorderData;
 
      /* Draw the border at 10,10 */
      IIntuition->DrawBorder(win->RPort,&shadowBorder,10,10);
 
      /* Draw the border again at 100,10 */
      IIntuition->DrawBorder(win->RPort,&shadowBorder,100,10);
 
      /* Wait a bit, then quit.
      ** In a real application, this would be an event loop, like the
      ** one described in the Intuition Input and Output Methods article.
      */
      IDOS->Delay(200);
 
      IIntuition->CloseWindow(win);
    }
  }
 
  IExec->DropInterface((struct Interface*)IIntuition);
  IExec->CloseLibrary(IntuitionBase);
 
  return 0;
}

Border Colors and Drawing Modes

Borders can select their colors from the values set in the color registers for the screen in which they are rendered. The available number of colors and palette settings are screen attributes and may not be changed through border rendering.

Two drawing modes pertain to border lines: JAM1 and COMPLEMENT. To draw the line in a specific color, use the JAM1 draw mode. This mode converts each pixel in the line to the color set in the FrontPen field.

Selecting the COMPLEMENT draw mode causes the line to be drawn in an exclusive-or mode that inverts the color of each pixel within the line. The data bits of the pixel are changed to their binary complement. This complement is formed by reversing all bits in the binary representation of the color register number. In a three bitplane display, for example, color 6 is 110 in binary. In COMPLEMENT draw mode, if a pixel is color 6, it will be changed to the 001 (binary), which is color 1. Note that a border drawn in COMPLEMENT mode can be removed from a static display by drawing the border again in the same position.

Border Coordinates

Intuition draws lines between points that are specified as sets of X, Y coordinates. Border data does not have to be in Chip memory.

The XY field contains a pointer to an array of coordinate pairs. All of these coordinates are offsets relative to the Border position, which is determined by the sum of the external and internal position components as described above. The coordinate pairs are ordered sequentially. The first two numbers make up the first coordinate pair, the next two numbers make up the second pair, and so on. Within a coordinate pair, the first number is the X offset and the second number is the Y offset.

The first coordinate pair describes the starting point of the first line. When the Border is rendered, a line is drawn between each pair of points. The first line is drawn from point one to point two, the second line is drawn from point two to point three, and so on, until the final point is reached.

The numbers specified in the XY array may be positive or negative. Negative values move up and to the left relative to the Border position, positive values move down and to the right. Again, the Border position is determined by adding the external position component and the internal position component. For example, a Border attached to a Gadget has an external component equal to the upper left corner of the gadget’s select box. The internal component is set within the Border structure itself. These two components are added together and offsets from the resulting position, specified within the XY array, determine where the lines of the Border will appear.

Suppose the top left corner of the select box of the gadget is at window position (10,5). If the Border has LeftEdge set to 10 and TopEdge set to 10, then the Border is positioned at (10+10,5+10), that is (20,15). All XY coordinates will be relative to this Border position. If the XY array contains ‘0,5, 15,5, 15,0’, then the relative coordinates will be (0,5), (15,5) and (15,0). Adding each coordinate to the Border position gives the absolute position of the lines within the window. This Border will draw two lines in the window, one from (20,20) to (35,20) and the second from (35,20) to (35,15).

Example of Border Relative Position

To create a border that is outside the select box of a gadget, specify negative values in the internal component or use negative values for the initial XY values. For example, setting LeftEdge to -1 and TopEdge to -1 moves the position of the Border one pixel above and one pixel to the left of the gadget’s select box.

Linking Borders

The NextBorder field can point to another instance of a Border structure. This allows complex graphic objects to be created by linking together Border structures, each with its own data points, color and draw mode. This might be used, for instance, to draw a double border around a requester or gadget where the outer border is a second Border structure, linked to the first inner border.

Note that the borders can share data. For instance, to create a border with a shadow, link two borders together each of which points to the same XY data. Set the first border to draw in a dark pen (such as the SHADOWPEN from the screen’s DrawInfo structure) and position the border down and to the right a few pixels by changing LeftEdge and TopEdge in the Border structure.

The second border should be set to a bright pen (such as the SHINEPEN in the screen’s DrawInfo structure). When the border is drawn, the first border will draw in a dark color and then the second border will be drawn over it in a light color. Since they use the same data set, and the dark border is shifted down and to the right, the border will have a three dimensional appearance. This technique is demonstrated in the example listed earlier in this section.