Copyright (c) Hyperion Entertainment and contributors.
Difference between revisions of "Graphics Primitives"
Steven Solie (talk | contribs) |
Steven Solie (talk | contribs) |
||
(39 intermediate revisions by the same user not shown) | |||
Line 2: | Line 2: | ||
== Graphics Primitives == |
== Graphics Primitives == |
||
− | This |
+ | This article describes the basic graphics functions available to Amiga programmers. It covers the graphics support structures, display routines and drawing routines. Many of the operations described in this section are also performed by the Intuition software. See the Intuition articles for more information. |
The Amiga supports several basic types of graphics routines: display routines, drawing routines, sprites and animation. These routines are very versatile and allow you to define any combination of drawing and display areas you may wish to use. |
The Amiga supports several basic types of graphics routines: display routines, drawing routines, sprites and animation. These routines are very versatile and allow you to define any combination of drawing and display areas you may wish to use. |
||
− | + | This section explains all of the available modes of drawing supported by the system software, including how to do the following: |
|
− | |||
− | * How to query the graphics system to find out what type of video monitor is attached and which graphics modes can be displayed on it. |
||
− | * How to identify the memory area that you wish to have displayed. |
||
− | * How to position the display area window to show only a certain portion of a larger drawing area. |
||
− | * How to split the screen into as many vertically stacked slices as you wish. |
||
− | * How to determine which horizontal and vertical resolution modes to use. |
||
− | * How to determine the current correct number of pixels across and lines down for a particular section of the display. |
||
− | * How to specify how many color choices per pixel are to be available in a specific section of the display. |
||
− | |||
− | The later sections of the chapter explain all of the available modes of drawing supported by the system software, including how to do the following: |
||
* Reserve memory space for use by the drawing routines. |
* Reserve memory space for use by the drawing routines. |
||
Line 30: | Line 20: | ||
* Use a template (predefined shape) to draw an object into a drawing area. |
* Use a template (predefined shape) to draw an object into a drawing area. |
||
− | [[Classic_Graphics_Primitives|Classic Graphics Primitives]] |
+ | For those working with Classic Amiga displays such as ECS and AGA you will want to reference [[Classic_Graphics_Primitives|Classic Graphics Primitives]]. |
== Drawing Routines == |
== Drawing Routines == |
||
Line 80: | Line 70: | ||
The sections that follow explain each of the items in the RastPort structure is used. |
The sections that follow explain each of the items in the RastPort structure is used. |
||
− | |||
− | ==== Initializing a BitMap Structure ==== |
||
− | |||
− | Associated with the RastPort is another data structure called a BitMap which contains a description of the organization of the data in the drawing area. This tells the graphics library where in memory the drawing area is located and how it is arranged. Before you can set up a RastPort for drawing you must first declare and initialize a BitMap structure, defining the characteristics of the drawing area, as shown in the following example. This was already shown in the section called "Forming a Basic Display", but it is repeated here because it relates to drawing as well as to display routines. (You need not necessarily use the same BitMap for both the drawing and the display, e.g., double-buffered displays.) |
||
− | |||
− | <syntaxhighlight> |
||
− | #define DEPTH 2 /* Two planes deep. */ |
||
− | #define WIDTH 320 /* Width in pixels. */ |
||
− | #define HEIGHT 200 /* Height in scanlines. */ |
||
− | |||
− | struct BitMap bitMap; |
||
− | |||
− | /* Initialize the BitMap. */ |
||
− | InitBitMap(&bitMap, DEPTH, WIDTH, HEIGHT); |
||
− | </syntaxhighlight> |
||
==== Initializing a RastPort Structure ==== |
==== Initializing a RastPort Structure ==== |
||
Line 151: | Line 126: | ||
The graphics element pointer in the RastPort structure is called GelsInfo. |
The graphics element pointer in the RastPort structure is called GelsInfo. |
||
− | If you are doing graphics animation using the GELS system, this pointer must refer to a properly initialized GelsInfo structure. See |
+ | If you are doing graphics animation using the GELS system, this pointer must refer to a properly initialized GelsInfo structure. See [[Graphics_Sprites,_Bobs_and_Animation|Graphics Sprites, Bobs and Animation]] for more information. |
==== RastPort Write Mask ==== |
==== RastPort Write Mask ==== |
||
Line 159: | Line 134: | ||
For most applications, this variable is set to all bits on. This means that all bitplanes defined in the BitMap are affected by a graphics writing operation. You can selectively disable one or more bitplanes by simply specifying a 0 bit in that specific position in the control byte. For example: |
For most applications, this variable is set to all bits on. This means that all bitplanes defined in the BitMap are affected by a graphics writing operation. You can selectively disable one or more bitplanes by simply specifying a 0 bit in that specific position in the control byte. For example: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
+ | uint32 result = IGraphics->SetWriteMask(&rastPort, 0xFB); /* disable bitplane 2 */ |
||
− | #include <graphics/gfxmacros.h> |
||
+ | if (result == 0) |
||
− | |||
+ | IDOS->Printf("Can't set write mask\n"); |
||
− | SetWrMsk(&rastPort, 0xFB); /* disable bitplane 2 */ |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
A useful application for the Mask field is to set or clear plane 6 while in the Extra-Half-Brite display mode to create shadow effects. For example: |
A useful application for the Mask field is to set or clear plane 6 while in the Extra-Half-Brite display mode to create shadow effects. For example: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | + | IGraphics->SetWriteMask(&rastPort, 0xE0); /* Disable planes 1 through 5. */ |
|
− | SetAPen(&rastPort, 0); /* Clear the Extra-Half-Brite bit */ |
+ | IGraphics->SetAPen(&rastPort, 0); /* Clear the Extra-Half-Brite bit */ |
− | RectFill(&rastPort, 20, 20, 40, 30); /* in the old rectangle. */ |
+ | IGraphics->RectFill(&rastPort, 20, 20, 40, 30); /* in the old rectangle. */ |
− | SetAPen(&rastPort, 32); /* Set the Extra-Half-Brite bit */ |
+ | IGraphics->SetAPen(&rastPort, 32); /* Set the Extra-Half-Brite bit */ |
− | RectFill(&rastPort, 30, 25, 50, 35); /* in the new rectangle. */ |
+ | IGraphics->RectFill(&rastPort, 30, 25, 50, 35); /* in the new rectangle. */ |
− | SetWrMsk(&rastPort, -1); /* Re-enable all planes. */ |
+ | IGraphics->SetWrMsk(&rastPort, -1); /* Re-enable all planes. */ |
+ | </syntaxhighlight> |
||
− | </pre> |
||
+ | |||
+ | {{Note|text=Do not use the obsolete SetWrMsk() macro in new code.}} |
||
==== RastPort Drawing Pens ==== |
==== RastPort Drawing Pens ==== |
||
Line 202: | Line 179: | ||
You establish the color for FgPen using the statement: |
You establish the color for FgPen using the statement: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | SetAPen(& |
+ | SetAPen(&rastPort, newcolor); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
The color in BgPen is used as the secondary drawing color for rendering lines and areas. If you specify that the drawing mode is JAM2 (jamming two colors) and a pattern is being drawn, the primary drawing color (FgPen) is used where there are 1s in the pattern. The secondary drawing color (BgPen) is used where there are 0s in the pattern. |
The color in BgPen is used as the secondary drawing color for rendering lines and areas. If you specify that the drawing mode is JAM2 (jamming two colors) and a pattern is being drawn, the primary drawing color (FgPen) is used where there are 1s in the pattern. The secondary drawing color (BgPen) is used where there are 0s in the pattern. |
||
Line 210: | Line 187: | ||
You establish the drawing color for BgPen using the statement: |
You establish the drawing color for BgPen using the statement: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | SetBPen(& |
+ | SetBPen(&rastPort, newcolor); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
The area outline pen AOlPen is used in two applications: area fill and flood fill. (See “Area Fill Operations” below.) In area fill, you can specify that an area, once filled, can be outlined in this AOlPen color. In flood fill (in one of its operating modes) you can fill until the flood-filler hits a pixel of the color specified in this pen variable. |
The area outline pen AOlPen is used in two applications: area fill and flood fill. (See “Area Fill Operations” below.) In area fill, you can specify that an area, once filled, can be outlined in this AOlPen color. In flood fill (in one of its operating modes) you can fill until the flood-filler hits a pixel of the color specified in this pen variable. |
||
Line 218: | Line 195: | ||
You establish the drawing color for AOlPen using the statement: |
You establish the drawing color for AOlPen using the statement: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | + | SetOutlinePen(&rastPort, newcolor); |
|
+ | </syntaxhighlight> |
||
− | </pre> |
||
+ | |||
+ | {{Note|text=Do not use the obsolete SetOPen() macro.}} |
||
==== RastPort Drawing Modes ==== |
==== RastPort Drawing Modes ==== |
||
Line 240: | Line 219: | ||
You set the drawing modes using the statement: |
You set the drawing modes using the statement: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | SetDrMd(& |
+ | SetDrMd(&rastPort, newmode); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
Set the newmode argument to one of the four drawing modes listed above. |
Set the newmode argument to one of the four drawing modes listed above. |
||
Line 254: | Line 233: | ||
For example, you can establish a dotted line pattern with the graphics macro SetDrPt(): |
For example, you can establish a dotted line pattern with the graphics macro SetDrPt(): |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | SetDrPt(& |
+ | SetDrPt(&rastPort, 0xCCCC); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
The second argument is a bit-pattern, 1100110011001100, to be applied to all lines drawn. If you draw multiple, connected lines, the pattern cleanly connects all the points. |
The second argument is a bit-pattern, 1100110011001100, to be applied to all lines drawn. If you draw multiple, connected lines, the pattern cleanly connects all the points. |
||
Line 262: | Line 241: | ||
The area pattern is also 16 bits wide and its height is some power of two. This means that you can define patterns in heights of 1, 2, 4, 8, 16, and so on. To tell the system how large a pattern you are providing, use the graphics macro SetAfPt(): |
The area pattern is also 16 bits wide and its height is some power of two. This means that you can define patterns in heights of 1, 2, 4, 8, 16, and so on. To tell the system how large a pattern you are providing, use the graphics macro SetAfPt(): |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | SetAfPt(& |
+ | SetAfPt(&rastPort, &areaPattern, power_of_two); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
The &areaPattern argument is the address of the first word of the area pattern and power_of_two specifies how many words are in the pattern. For example: |
The &areaPattern argument is the address of the first word of the area pattern and power_of_two specifies how many words are in the pattern. For example: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | + | uint16 ditherData[] = |
|
{ |
{ |
||
0x5555, 0xAAAA |
0x5555, 0xAAAA |
||
}; |
}; |
||
− | SetAfPt(& |
+ | SetAfPt(&rastPort, ditherData, 1); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
This example produces a small cross-hatch pattern, useful for shading. Because power_of_two is set to 1, the pattern height is 2 to the 1st, or 2 rows high. |
This example produces a small cross-hatch pattern, useful for shading. Because power_of_two is set to 1, the pattern height is 2 to the 1st, or 2 rows high. |
||
Line 281: | Line 260: | ||
To clear the area fill pattern, use: |
To clear the area fill pattern, use: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | SetAfPt(& |
+ | SetAfPt(&rastPort, NULL, 0); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
{{Note|title=Pattern Positioning|text=The pattern is always positioned with respect to the upper left corner of the RastPort drawing area (the ''0,0'' coordinate). If you draw two rectangles whose edges are adjacent, the pattern will be continuous across the rectangle boundaries.}} |
{{Note|title=Pattern Positioning|text=The pattern is always positioned with respect to the upper left corner of the RastPort drawing area (the ''0,0'' coordinate). If you draw two rectangles whose edges are adjacent, the pattern will be continuous across the rectangle boundaries.}} |
||
Line 289: | Line 268: | ||
The last example given produces a two-color pattern with one color where there are 1s and the other color where there are 0s in the pattern. A special mode allows you to develop a pattern having up to 256 colors. To create this effect, specify power_of_two as a negative value instead of a positive value. For instance, the following initialization establishes an 8-color checkerboard pattern where each square in the checkerboard has a different color. |
The last example given produces a two-color pattern with one color where there are 1s and the other color where there are 0s in the pattern. A special mode allows you to develop a pattern having up to 256 colors. To create this effect, specify power_of_two as a negative value instead of a positive value. For instance, the following initialization establishes an 8-color checkerboard pattern where each square in the checkerboard has a different color. |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | + | uint16 areaPattern[3][8] = |
|
{ |
{ |
||
/* plane 0 pattern */ |
/* plane 0 pattern */ |
||
Line 315: | Line 294: | ||
}; |
}; |
||
− | SetAfPt(& |
+ | SetAfPt(&rastPort, areaPattern, -3); |
/* when doing this, it is best to set three other parameters as follows: */ |
/* when doing this, it is best to set three other parameters as follows: */ |
||
− | SetAPen(& |
+ | SetAPen(&rastPort, -1); |
− | SetBPen(& |
+ | SetBPen(&rastPort, 0); |
− | SetDrMd(& |
+ | SetDrMd(&rastPort, JAM2); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
If you use this multicolored pattern mode, you must provide as many planes of pattern data as there are planes in your BitMap. |
If you use this multicolored pattern mode, you must provide as many planes of pattern data as there are planes in your BitMap. |
||
Line 335: | Line 314: | ||
==== Text Attributes ==== |
==== Text Attributes ==== |
||
− | Text attributes and font information are stored in the RastPort fields Font, AlgoStyle, TxFlags, TxHeight, TxWidth, TxBaseline and TxSpacing. These are normally set by calls to the graphics font routines which are covered separately in |
+ | Text attributes and font information are stored in the RastPort fields Font, AlgoStyle, TxFlags, TxHeight, TxWidth, TxBaseline and TxSpacing. These are normally set by calls to the graphics font routines which are covered separately in [[Graphics Library and Text]]. |
+ | |||
+ | ==== Working with 32 bit colours on direct mapped screens ==== |
||
+ | |||
+ | As well as the individual functions for setting the various RastPort attributes (e.g. SetAPen(), SetBPen()) you can also use the function SetRPAttrs(). SetRPAttrs() also allows you to define multiple attributes in a single call using a tag list. |
||
+ | |||
+ | When working with 32 bit colour values use SetRPAttrs() with the tags RPTAG_APenColor, RPTAG_BPenColor and RPTAG_OPenColor. This of course only works on direct mapped bitmaps (e.g. 16bit or 32bit). |
||
+ | |||
+ | The colour format used by the three tags above is 0xAARRGGBB. The alpha part is currently ignored and should be set to 0xFF so that the correct alpha is used when alpha blending has been implemented. |
||
+ | |||
+ | The following code snippet might be used to render a light blue and yellow dotted line using 32bit colours. |
||
+ | |||
+ | <syntaxhighlight> |
||
+ | IGraphics->SetRPAttrs(rp, |
||
+ | RPTAG_APenColor, 0xFF9999FF, // light blue |
||
+ | RPTAG_BPenColor, 0xFFFFFF00, // yellow |
||
+ | RPTAG_DrMd, JAM2, // use two colours |
||
+ | TAG_END); |
||
+ | |||
+ | SetDrPt(rp, 0xFF00); // NB this one is a macro so no 'IGraphics' |
||
+ | |||
+ | IGraphics->Move(rp, x1, y1); |
||
+ | IGraphics->Draw(rp, x2, y2); |
||
+ | </syntaxhighlight> |
||
=== Using the Graphics Drawing Routines === |
=== Using the Graphics Drawing Routines === |
||
− | This section shows you how to use the Amiga drawing routines. All of these routines work either on their own or along with the windowing system and layers library. For details about using the layers and windows, see |
+ | This section shows you how to use the Amiga drawing routines. All of these routines work either on their own or along with the windowing system and layers library. For details about using the layers and windows, see [[Layers Library]] and [[Intuition Windows]]. |
{{Note|title=Use WaitBlit()|text=The graphics library rendering and data movement routines generally wait to get access to the blitter, start their blit, and then exit. Therefore, you must WaitBlit() after a graphics rendering or data movement call if you intend to immediately deallocate, examine, or perform order-dependent processor operations on the memory used in the call.}} |
{{Note|title=Use WaitBlit()|text=The graphics library rendering and data movement routines generally wait to get access to the blitter, start their blit, and then exit. Therefore, you must WaitBlit() after a graphics rendering or data movement call if you intend to immediately deallocate, examine, or perform order-dependent processor operations on the memory used in the call.}} |
||
Line 345: | Line 347: | ||
As you read this section, keep in mind that to use the drawing routines, you need to pass them a pointer to a RastPort. You can define the RastPort directly, as shown in the sample program segments in preceding sections, or you can get a RastPort from your Window structure using code like the following: |
As you read this section, keep in mind that to use the drawing routines, you need to pass them a pointer to a RastPort. You can define the RastPort directly, as shown in the sample program segments in preceding sections, or you can get a RastPort from your Window structure using code like the following: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
struct Window *window; |
struct Window *window; |
||
struct RastPort *rastPort; |
struct RastPort *rastPort; |
||
− | window = OpenWindow(& |
+ | window = IIntuition->OpenWindow(&newWindow); /* You could use OpenWindowTags() instead. */ |
if (window) |
if (window) |
||
− | rastPort = window- |
+ | rastPort = window->RPort; |
+ | </syntaxhighlight> |
||
− | </pre> |
||
You can also get the RastPort from the Layer structure, if you are not using Intuition. |
You can also get the RastPort from the Layer structure, if you are not using Intuition. |
||
Line 360: | Line 362: | ||
You can set a specific pixel to a desired color by using a statement like this: |
You can set a specific pixel to a desired color by using a statement like this: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | + | int16 x, y; |
|
+ | int32 result = IGraphics->WritePixel(&rastPort, x, y); |
||
− | LONG result; |
||
+ | </syntaxhighlight> |
||
− | result = WritePixel(&rastPort, x, y); |
||
− | </pre> |
||
WritePixel() uses the primary drawing pen and changes the pixel at that ''x,y'' position to the desired color if the ''x,y'' coordinate falls within the boundaries of the RastPort. A value of 0 is returned if the write was successful; a value of -1 is returned if ''x,y'' was outside the range of the RastPort. |
WritePixel() uses the primary drawing pen and changes the pixel at that ''x,y'' position to the desired color if the ''x,y'' coordinate falls within the boundaries of the RastPort. A value of 0 is returned if the write was successful; a value of -1 is returned if ''x,y'' was outside the range of the RastPort. |
||
Line 372: | Line 373: | ||
You can determine the color of a specific pixel with a statement like this: |
You can determine the color of a specific pixel with a statement like this: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | + | int16 x, y; |
|
+ | int32 result = IGraphics->ReadPixel(&rastPort, x, y); |
||
− | LONG result; |
||
+ | </syntaxhighlight> |
||
− | result = ReadPixel(&rastPort, x, y); |
||
− | </pre> |
||
ReadPixel() returns the value of the pixel color selector at the specified ''x,y'' location. If the coordinates you specify are outside the range of your RastPort, this function returns a value of -1. |
ReadPixel() returns the value of the pixel color selector at the specified ''x,y'' location. If the coordinates you specify are outside the range of your RastPort, this function returns a value of -1. |
||
Line 384: | Line 384: | ||
Two functions are associated with drawing ellipses: DrawCircle() and DrawEllipse(). DrawCircle(), a macro that calls DrawEllipse(), will draw a circle from the specified center point using the specified radius. This function is executed by the statement: |
Two functions are associated with drawing ellipses: DrawCircle() and DrawEllipse(). DrawCircle(), a macro that calls DrawEllipse(), will draw a circle from the specified center point using the specified radius. This function is executed by the statement: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | DrawCircle(& |
+ | DrawCircle(&rastPort, center_x, center_y, radius); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
Similarly, DrawEllipse() draws an ellipse with the specified radii from the specified center point: |
Similarly, DrawEllipse() draws an ellipse with the specified radii from the specified center point: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | DrawEllipse(& |
+ | DrawEllipse(&rastPort, center_x, center_y, horiz_r, vert_r); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
Neither function performs clipping on a non-layered RastPort. |
Neither function performs clipping on a non-layered RastPort. |
||
Line 402: | Line 402: | ||
Move() simply moves the cursor to a new position. It is like picking up a drawing pen and placing it at a new location. This function is executed by the statement: |
Move() simply moves the cursor to a new position. It is like picking up a drawing pen and placing it at a new location. This function is executed by the statement: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | Move(& |
+ | Move(&rastPort, x, y); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
Draw() draws a line from the current ''x,y'' position to a new ''x,y'' position specified in the statement itself. The drawing pen is left at the new position. This is done by the statement: |
Draw() draws a line from the current ''x,y'' position to a new ''x,y'' position specified in the statement itself. The drawing pen is left at the new position. This is done by the statement: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | Draw(& |
+ | Draw(&rastPort, x, y); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
Draw() uses the pen color specified for FgPen. |
Draw() uses the pen color specified for FgPen. |
||
Line 416: | Line 416: | ||
Here is a sample sequence that draws a line from location ''(0,0)'' to ''(100,50)''. |
Here is a sample sequence that draws a line from location ''(0,0)'' to ''(100,50)''. |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | SetAPen(& |
+ | IGraphics->SetAPen(&rastPort, COLOR1); /* Set A pen color. */ |
− | Move(& |
+ | IGraphics->Move(&rastPort, 0, 0); /* Move to this location. */ |
− | Draw(& |
+ | IGraphics->Draw(&rastPort, 100, 50); /* Draw to a this location. */ |
+ | </syntaxhighlight> |
||
− | </pre> |
||
{{Note|title=Caution|text=If you attempt to draw a line outside the bounds of the BitMap, using the basic initialized RastPort, you may crash the system. You must either do your own software clipping to assure that the line is in range, or use the layers library. Software clipping means that you need to determine if the line will fall outside your BitMap ''before'' you draw it, and render only the part which falls inside the BitMap.}} |
{{Note|title=Caution|text=If you attempt to draw a line outside the bounds of the BitMap, using the basic initialized RastPort, you may crash the system. You must either do your own software clipping to assure that the line is in range, or use the layers library. Software clipping means that you need to determine if the line will fall outside your BitMap ''before'' you draw it, and render only the part which falls inside the BitMap.}} |
||
Line 428: | Line 428: | ||
To turn the example above into a patterned line draw, simply set a drawing pattern, such as: |
To turn the example above into a patterned line draw, simply set a drawing pattern, such as: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | SetDrPt(& |
+ | SetDrPt(&rastPort, 0xAAAA); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
Now all lines drawn appear as dotted lines (0xAAAA = 1010101010101010 in binary). To resume drawing solid lines, execute the statement: |
Now all lines drawn appear as dotted lines (0xAAAA = 1010101010101010 in binary). To resume drawing solid lines, execute the statement: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | SetDrPt(& |
+ | SetDrPt(&rastPort, ~0); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
Because ~0 is defined as all bits on (11...11) in binary. |
Because ~0 is defined as all bits on (11...11) in binary. |
||
Line 446: | Line 446: | ||
If the shapes are all definable as interconnected, continuous lines, you can use a simpler function, called PolyDraw(). PolyDraw() takes a set of line endpoints and draws a shape using these points. You call PolyDraw() with the statement: |
If the shapes are all definable as interconnected, continuous lines, you can use a simpler function, called PolyDraw(). PolyDraw() takes a set of line endpoints and draws a shape using these points. You call PolyDraw() with the statement: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | PolyDraw(& |
+ | PolyDraw(&rastPort, count, arraypointer); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
PolyDraw() reads the array of points and draws a line from the first pair of coordinates to the second, then a connecting line to each succeeding pair in the array until count points have been connected. This function uses the current drawing mode, pens, line pattern, and write mask specified in the target RastPort; for example, this fragment draws a rectangle, using the five defined pairs of ''x,y'' coordinates. |
PolyDraw() reads the array of points and draws a line from the first pair of coordinates to the second, then a connecting line to each succeeding pair in the array until count points have been connected. This function uses the current drawing mode, pens, line pattern, and write mask specified in the target RastPort; for example, this fragment draws a rectangle, using the five defined pairs of ''x,y'' coordinates. |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | + | int16 linearray[] = |
|
{ |
{ |
||
3, 3, |
3, 3, |
||
Line 462: | Line 462: | ||
}; |
}; |
||
− | PolyDraw(& |
+ | IGraphics->PolyDraw(&rastPort, 5, linearray); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
==== Area-fill Operations ==== |
==== Area-fill Operations ==== |
||
Line 473: | Line 473: | ||
AreaMove() is executed with the statement: |
AreaMove() is executed with the statement: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
+ | int32 result = IGraphics->AreaMove(&rastPort, x, y); |
||
− | LONG result; |
||
+ | </syntaxhighlight> |
||
− | result = AreaMove(&rastPort, x, y); |
||
− | </pre> |
||
AreaMove() returns 0 if successful, -1 if there was no more space left in the vector list. |
AreaMove() returns 0 if successful, -1 if there was no more space left in the vector list. |
||
Line 482: | Line 481: | ||
AreaDraw() tells the system to add a new vertex to a list that it is building. No drawing takes place until AreaEnd() is executed. AreaDraw is executed with the statement: |
AreaDraw() tells the system to add a new vertex to a list that it is building. No drawing takes place until AreaEnd() is executed. AreaDraw is executed with the statement: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
+ | int32 result = IGraphics->AreaDraw(&rastPort, x, y); |
||
− | LONG result; |
||
+ | </syntaxhighlight> |
||
− | result = AreaDraw(&rastPort, x, y); |
||
− | </pre> |
||
AreaDraw() returns 0 if successful, -1 if there was no more space left in the vector list. |
AreaDraw() returns 0 if successful, -1 if there was no more space left in the vector list. |
||
Line 493: | Line 491: | ||
To fill an area, you do not have to AreaDraw() back to the first point before calling AreaEnd(). AreaEnd() automatically closes the polygon. AreaEnd() is executed with the following statement: |
To fill an area, you do not have to AreaDraw() back to the first point before calling AreaEnd(). AreaEnd() automatically closes the polygon. AreaEnd() is executed with the following statement: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | + | int32 result = AreaEnd(&rastPort); |
|
+ | </syntaxhighlight> |
||
− | result = AreaEnd(&rastPort); |
||
− | </pre> |
||
AreaEnd() returns 0 if successful, -1 if there was an error. |
AreaEnd() returns 0 if successful, -1 if there was an error. |
||
Line 502: | Line 499: | ||
To turn off the outline function, you have to set the RastPort Flags variable back to 0 with BNDRYOFF(): |
To turn off the outline function, you have to set the RastPort Flags variable back to 0 with BNDRYOFF(): |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | #include |
+ | #include <graphics/gfxmacros.h> |
− | BNDRYOFF(& |
+ | BNDRYOFF(&rastPort); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
Otherwise, every subsequent area-fill or rectangle-fill operation will outline their rendering with the outline pen (AOlPen). |
Otherwise, every subsequent area-fill or rectangle-fill operation will outline their rendering with the outline pen (AOlPen). |
||
Line 514: | Line 511: | ||
Two functions are associated with drawing filled ellipses: AreaCircle() and AreaEllipse(). AreaCircle() (a macro that calls AreaEllipse()) will draw a circle from the specified center point using the specified radius. This function is executed by the statement: |
Two functions are associated with drawing filled ellipses: AreaCircle() and AreaEllipse(). AreaCircle() (a macro that calls AreaEllipse()) will draw a circle from the specified center point using the specified radius. This function is executed by the statement: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | AreaCircle(& |
+ | IGraphics->AreaCircle(&rastPort, center_x, center_y, radius); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
Similarly, AreaEllipse() draws a filled ellipse with the specified radii from the specified center point: |
Similarly, AreaEllipse() draws a filled ellipse with the specified radii from the specified center point: |
||
<pre> |
<pre> |
||
− | AreaEllipse(& |
+ | IGraphics->AreaEllipse(&rastPort, center_x, center_y, horiz_r, vert_r); |
</pre> |
</pre> |
||
− | Outlining with |
+ | Outlining with SetOutlinePen() is not currently supported by the AreaCircle() and AreaEllipse() routines. |
{{Note|title=Caution|text=If you attempt to fill an area outside the bounds of the BitMap, using the basic initialized RastPort, it may crash the system. You must either do your own software clipping to assure that the area is in range, or use the layers library.}} |
{{Note|title=Caution|text=If you attempt to fill an area outside the bounds of the BitMap, using the basic initialized RastPort, it may crash the system. You must either do your own software clipping to assure that the area is in range, or use the layers library.}} |
||
Line 536: | Line 533: | ||
You use the Flood() routine for flood fill. The syntax for this routine is as follows: |
You use the Flood() routine for flood fill. The syntax for this routine is as follows: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | Flood(& |
+ | IGraphics->Flood(&rastPort, mode, x, y); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
The rastPort argument specifies the RastPort you want to draw into. The x and y arguments specify the starting coordinate within the BitMap. The mode argument tells how to do the fill. There are two different modes for flood fill: |
The rastPort argument specifies the RastPort you want to draw into. The x and y arguments specify the starting coordinate within the BitMap. The mode argument tells how to do the fill. There are two different modes for flood fill: |
||
Line 548: | Line 545: | ||
The following sample program fragment creates and then flood-fills a triangular region. The overall effect is exactly the same as shown in the preceding area-fill example above, except that flood-fill is slightly slower than area-fill. Mode 0 (fill to a pixel that has the color of the outline pen) is used in the example. |
The following sample program fragment creates and then flood-fills a triangular region. The overall effect is exactly the same as shown in the preceding area-fill example above, except that flood-fill is slightly slower than area-fill. Mode 0 (fill to a pixel that has the color of the outline pen) is used in the example. |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | + | int8 oldAPen; |
|
− | + | uint16 oldDrPt; |
|
− | struct RastPort *rastPort = Window- |
+ | struct RastPort *rastPort = Window->RPort; |
/* Save the old values of the foreground pen and draw pattern. */ |
/* Save the old values of the foreground pen and draw pattern. */ |
||
− | oldAPen = rastPort- |
+ | oldAPen = rastPort->FgPen; |
− | oldDrPt = rastPort- |
+ | oldDrPt = rastPort->LinePtrn; |
/* Use AreaOutline pen color for foreground pen. */ |
/* Use AreaOutline pen color for foreground pen. */ |
||
− | SetAPen(rastPort, rastPort- |
+ | IGraphics->SetAPen(rastPort, rastPort->AOlPen); |
− | SetDrPt(rastPort, ~0); /* Insure a solid draw pattern. */ |
+ | IGraphics->SetDrPt(rastPort, ~0); /* Insure a solid draw pattern. */ |
− | Move(rastPort, 0, 0); /* Using mode 0 to create a triangular shape */ |
+ | IGraphics->Move(rastPort, 0, 0); /* Using mode 0 to create a triangular shape */ |
− | Draw(rastPort, 0, 100); |
+ | IGraphics->Draw(rastPort, 0, 100); |
− | Draw(rastPort, 100, 100); |
+ | IGraphics->Draw(rastPort, 100, 100); |
− | Draw(rastPort, 0, 0); /* close it */ |
+ | IGraphics->Draw(rastPort, 0, 0); /* close it */ |
− | SetAPen(rastPort, oldAPen); /* Restore original foreground pen. */ |
+ | IGraphics->SetAPen(rastPort, oldAPen); /* Restore original foreground pen. */ |
− | Flood(rastPort, 0, 10, 50); /* Start Flood() inside triangle. */ |
+ | IGraphics->Flood(rastPort, 0, 10, 50); /* Start Flood() inside triangle. */ |
SetDrPt(rastPort, oldDrPt); /* Restore original draw mode. */ |
SetDrPt(rastPort, oldDrPt); /* Restore original draw mode. */ |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
This example saves the current FgPen value and draws the shape in the same color as AOlPen. |
This example saves the current FgPen value and draws the shape in the same color as AOlPen. |
||
Line 580: | Line 577: | ||
The final fill function, RectFill(), is for filling rectangular areas. The form of this function follows: |
The final fill function, RectFill(), is for filling rectangular areas. The form of this function follows: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | RectFill(& |
+ | RectFill(&rastPort, xmin, ymin, xmax, ymax); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
As usual, the rastPort argument specifies the RastPort you want to draw into. The xmin and ymin arguments specify the upper left corner of the rectangle to be filled. The xmax and ymax arguments specify the lower right corner of the rectangle to be filled. Note that the variable xmax must be equal to or greater than xmin, and ymax must be equal to or greater than ymin. |
As usual, the rastPort argument specifies the RastPort you want to draw into. The xmin and ymin arguments specify the upper left corner of the rectangle to be filled. The xmax and ymax arguments specify the lower right corner of the rectangle to be filled. Note that the variable xmax must be equal to or greater than xmin, and ymax must be equal to or greater than ymin. |
||
Line 597: | Line 594: | ||
* Set a raster to a specific color |
* Set a raster to a specific color |
||
* Scroll a subrectangle of a raster |
* Scroll a subrectangle of a raster |
||
− | * Draw a pattern |
+ | * Draw a pattern "through a stencil" |
* Extract a pattern from a bit-packed array and draw it into a raster |
* Extract a pattern from a bit-packed array and draw it into a raster |
||
* Copy rectangular regions from one bitmap to another |
* Copy rectangular regions from one bitmap to another |
||
Line 610: | Line 607: | ||
For memory that is accessible to the blitter (that is, internal Chip memory), the most efficient way to clear a range of memory is to use the blitter. You use the blitter to clear a block of memory with the statement: |
For memory that is accessible to the blitter (that is, internal Chip memory), the most efficient way to clear a range of memory is to use the blitter. You use the blitter to clear a block of memory with the statement: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
BltClear(memblock, bytecount, flags); |
BltClear(memblock, bytecount, flags); |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
The memblock argument is a pointer to the location of the first byte to be cleared and bytecount is the number of bytes to set to zero. In general the flags variable should be set to one to wait for the blitter operation to complete. Refer to the SDK for other details about the flag argument. |
The memblock argument is a pointer to the location of the first byte to be cleared and bytecount is the number of bytes to set to zero. In general the flags variable should be set to one to wait for the blitter operation to complete. Refer to the SDK for other details about the flag argument. |
||
Line 622: | Line 619: | ||
A call to this function takes the following form: |
A call to this function takes the following form: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | SetRast(& |
+ | SetRast(&rastPort, pen); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
As always, the &rastPort is a pointer to the RastPort you wish to use. Set the pen argument to the color register you want to fill the RastPort with. |
As always, the &rastPort is a pointer to the RastPort you wish to use. Set the pen argument to the color register you want to fill the RastPort with. |
||
Line 638: | Line 635: | ||
Here is the syntax of the ScrollRaster() function: |
Here is the syntax of the ScrollRaster() function: |
||
+ | <syntaxhighlight> |
||
− | <pre>ScrollRaster(&rastPort, dx, dy, xmin, ymin, xmax, ymax);</pre> |
||
+ | ScrollRaster(&rastPort, dx, dy, xmin, ymin, xmax, ymax); |
||
+ | </syntaxhighlight> |
||
+ | |||
The &rastPort argument is a pointer to a RastPort. The dx and dy arguments are the distances (positive, 0, or negative) to move the rectangle. The outer bounds of the sub-rectangle are defined by the xmin, xmax, ymin and ymax arguments. |
The &rastPort argument is a pointer to a RastPort. The dx and dy arguments are the distances (positive, 0, or negative) to move the rectangle. The outer bounds of the sub-rectangle are defined by the xmin, xmax, ymin and ymax arguments. |
||
Here are some examples that scroll a sub-rectangle: |
Here are some examples that scroll a sub-rectangle: |
||
+ | <syntaxhighlight> |
||
− | <pre>/* scroll up 2 */ |
||
+ | /* scroll up 2 */ |
||
− | ScrollRaster(&rastPort, 0, 2, 10, 10, 50, 50); |
||
+ | ScrollRaster(&rastPort, 0, 2, 10, 10, 50, 50); |
||
/* scroll right 1 */ |
/* scroll right 1 */ |
||
− | ScrollRaster(& |
+ | ScrollRaster(&rastPort, -1, 0, 10, 10, 50, 50); |
+ | </syntaxhighlight> |
||
− | When scrolling a Simple Refresh window (or other layered RastPort), ScrollRaster() scrolls the appropriate existing damage region. Refer to the “Intuition Windows” chapter for an explanation of Simple Refresh windows and damage regions. |
||
+ | When scrolling a Simple Refresh window (or other layered RastPort), ScrollRaster() scrolls the appropriate existing damage region. Refer to [[Intuition Windows]] for an explanation of Simple Refresh windows and damage regions. |
||
− | When scrolling a SuperBitMap window ScrollRaster() requires a properly initialized TmpRas. The TmpRas must be initialized to the size of one bitplane with a width and height the same as the SuperBitMap, using the technique described in the “Area-Fill Information” section above. |
||
+ | When scrolling a SuperBitMap window ScrollRaster() requires a properly initialized TmpRas. The TmpRas must be initialized to the size of one bitplane with a width and height the same as the SuperBitMap, using the technique described in the "Area-Fill Information" section above. |
||
− | If you are using a SuperBitMap Layer, it is possible that the information in the BitMap is not fully reflected in the layer and vice-versa. Two graphics calls, CopySBitMap() and SyncSBitMap(), remedy these situations. Again, refer to the “Intuition Windows” chapter for more on this. |
||
+ | |||
+ | If you are using a SuperBitMap Layer, it is possible that the information in the BitMap is not fully reflected in the layer and vice-versa. Two graphics calls, CopySBitMap() and SyncSBitMap(), remedy these situations. Again, refer to [[Intuition Windows]] for more on this. |
||
==== Drawing through a Stencil ==== |
==== Drawing through a Stencil ==== |
||
The routine BltPattern() allows you to change only a very selective portion of a drawing area. |
The routine BltPattern() allows you to change only a very selective portion of a drawing area. |
||
− | |||
− | |||
Basically, this routine lets you define a rectangular region to be affected by a drawing operation and a mask of the same size that further defines which pixels within the rectangle will be affected. |
Basically, this routine lets you define a rectangular region to be affected by a drawing operation and a mask of the same size that further defines which pixels within the rectangle will be affected. |
||
Line 664: | Line 665: | ||
The figure below shows an example of what you can do with BltPattern(). The 0 bits are represented by blank rectangles; the 1 bits by filled-in rectangles. |
The figure below shows an example of what you can do with BltPattern(). The 0 bits are represented by blank rectangles; the 1 bits by filled-in rectangles. |
||
− | [[File:LibFig27-17.png]] |
+ | [[File:LibFig27-17.png|frame|center|Example of Drawing Through a Stencil]] |
− | |||
− | Figure 27-17: Example of Drawing Through a Stencil |
||
In the resulting drawing, the lighter squares show where the target drawing area has been affected. Exactly ''what'' goes into the drawing area when the mask has 1’s is determined by your RastPort’s FgPen, BgPen, DrawMode and AreaPtrn fields. |
In the resulting drawing, the lighter squares show where the target drawing area has been affected. Exactly ''what'' goes into the drawing area when the mask has 1’s is determined by your RastPort’s FgPen, BgPen, DrawMode and AreaPtrn fields. |
||
Line 672: | Line 671: | ||
You call BltPattern() with: |
You call BltPattern() with: |
||
+ | <syntaxhighlight> |
||
− | <pre>BltPattern(&rastport, mask, xl, yl, maxx, maxy, bytecnt)</pre> |
||
+ | BltPattern(&rastport, mask, xl, yl, maxx, maxy, bytecnt) |
||
+ | </syntaxhighlight> |
||
+ | |||
The &rastport argument specifies the RastPort to use. The operation will be confined to a rectangular area within the RastPort specified by xl and yl (upper right corner of the rectangle) and maxx and maxy (lower right corner of the rectangle). |
The &rastport argument specifies the RastPort to use. The operation will be confined to a rectangular area within the RastPort specified by xl and yl (upper right corner of the rectangle) and maxx and maxy (lower right corner of the rectangle). |
||
Line 685: | Line 687: | ||
The following figure shows an example. |
The following figure shows an example. |
||
− | [[File:LibFig27-18.png]] |
+ | [[File:LibFig27-18.png|frame|center|Example of Extracting from a Bit-Packed Array]] |
− | |||
− | Figure 27-18: Example of Extracting from a Bit-Packed Array |
||
For a rectangular bit array to be extracted from within a larger, rectangular bit array, the system must know how the larger array is organized. For this extraction to occur properly, you also need to tell the system the modulo for the inner rectangle. |
For a rectangular bit array to be extracted from within a larger, rectangular bit array, the system must know how the larger array is organized. For this extraction to occur properly, you also need to tell the system the modulo for the inner rectangle. |
||
Line 697: | Line 697: | ||
The modulo in this instance is 4, because at the end of each line, you must add 4 to the address pointer to make it point to the first word in the smaller rectangle. |
The modulo in this instance is 4, because at the end of each line, you must add 4 to the address pointer to make it point to the first word in the smaller rectangle. |
||
− | [[File:LibFig27-19.png]] |
+ | [[File:LibFig27-19.png|frame|center|Modulo]] |
− | |||
− | Figure 27-19: Modulo |
||
{{Note|title=Warning|text=The modulo value must be an even number of bytes.}} |
{{Note|title=Warning|text=The modulo value must be an even number of bytes.}} |
||
Line 705: | Line 703: | ||
BltTemplate() takes the following arguments: |
BltTemplate() takes the following arguments: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
BltTemplate(source, srcX, srcMod, &destRastPort, destX, destY, sizeX, sizeY); |
BltTemplate(source, srcX, srcMod, &destRastPort, destX, destY, sizeX, sizeY); |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
The source argument specifies the rectangular bit array to use as the source template. Set this to the address of the nearest word (rounded down) that contains the first line of the source rectangle. The srcX argument gives the exact bit position (0-15) within the source address at which the rectangular bit array begins. The srcMod argument sets the source modulo so the next line of the rectangular bit array can be found. |
The source argument specifies the rectangular bit array to use as the source template. Set this to the address of the nearest word (rounded down) that contains the first line of the source rectangle. The srcX argument gives the exact bit position (0-15) within the source address at which the rectangular bit array begins. The srcMod argument sets the source modulo so the next line of the rectangular bit array can be found. |
||
Line 719: | Line 717: | ||
==== Copying Rectangular Areas ==== |
==== Copying Rectangular Areas ==== |
||
+ | '''Need BltBitMapTagList() documentation added here.''' |
||
− | Four routines use the blitter to copy rectangular areas from one section of a BitMap to another: BltBitMap(), BltMaskBitMapRastPort(), BltBitMapRastPort(), and ClipBlit(). All four of these blitter routines take a special argument called a ''minterm''. |
||
+ | |||
+ | Four routines use the blitter to copy rectangular areas from one section of a BitMap to another: BltBitMap(), BltMaskBitMapRastPort(), BltBitMapRastPort() and ClipBlit(). All four of these blitter routines take a special argument called a ''minterm''. |
||
The minterm variable is an unsigned byte value which represents an action to be performed during the move. Since all the blitter routines uses the hardware blitter to move the data, they can take advantage of the blitter’s ability to logically combine or change the data as the move is made. The most common operation is a direct copy from source area to destination, which uses a minterm set to hex value C0. |
The minterm variable is an unsigned byte value which represents an action to be performed during the move. Since all the blitter routines uses the hardware blitter to move the data, they can take advantage of the blitter’s ability to logically combine or change the data as the move is made. The most common operation is a direct copy from source area to destination, which uses a minterm set to hex value C0. |
||
Line 774: | Line 774: | ||
BltBitMap() allows you to define a rectangle within a source BitMap and copy it to a destination area of the same size in another (or even the same) BitMap. This routine is used by the graphics library itself for rendering. BltBitMap() returns the number of planes actually involved in the blit. The syntax for the function is: |
BltBitMap() allows you to define a rectangle within a source BitMap and copy it to a destination area of the same size in another (or even the same) BitMap. This routine is used by the graphics library itself for rendering. BltBitMap() returns the number of planes actually involved in the blit. The syntax for the function is: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | + | uint32 planes; |
|
− | planes = BltBitMap(& |
+ | planes = BltBitMap(&srcBM, srcX, srcY, &dstBM, dstX, dstY, |
sizeX, sizeY, minterm, mask, tempA); |
sizeX, sizeY, minterm, mask, tempA); |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
The source bitmap is specified by the &srcBM argument. The position of the source area within the bitmap is specified by srcX and srcY. The destination bitmap is specified by the &dstBM argument. The position of the destination area within the bitmap is specified by dstX and dstY. |
The source bitmap is specified by the &srcBM argument. The position of the source area within the bitmap is specified by srcX and srcY. The destination bitmap is specified by the &dstBM argument. The position of the destination area within the bitmap is specified by dstX and dstY. |
||
− | The dimensions (in pixels) of the area to be moved is indicated by the sizeX and sizeY arguments. With the original custom chip set, the blitter size limits are |
+ | The dimensions (in pixels) of the area to be moved is indicated by the sizeX and sizeY arguments. With the original custom chip set, the blitter size limits are 992 x 1,024. With ECS the blitter size limits are 32,736 x 32,768. See the section on [[Classic_Graphics_Primitives#Determining_Chip_Versions|Determining Chip Versions]] to find out how to tell if the host system has ECS installed. |
The minterm argument determines what logical operation to perform on the rectangle data as bits are moved (described above). The mask argument, normally set to 0xff, specifies which bitplanes will be involved in the blit operation and which will be ignored. If a bit is set in the mask byte, the corresponding bitplane is included. The tempA argument applies only to blits that overlap and, if non-NULL, points to Chip memory the system will use for temporary storage during the blit. |
The minterm argument determines what logical operation to perform on the rectangle data as bits are moved (described above). The mask argument, normally set to 0xff, specifies which bitplanes will be involved in the blit operation and which will be ignored. If a bit is set in the mask byte, the corresponding bitplane is included. The tempA argument applies only to blits that overlap and, if non-NULL, points to Chip memory the system will use for temporary storage during the blit. |
||
Line 789: | Line 789: | ||
BltBitMapRastPort() takes most of the same arguments as BltBitMap(), but its destination is a RastPort instead of a BitMap. The syntax for the function is: |
BltBitMapRastPort() takes most of the same arguments as BltBitMap(), but its destination is a RastPort instead of a BitMap. The syntax for the function is: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | VOID BltBitMapRastPort(& |
+ | VOID BltBitMapRastPort(&srcBM, srcX, srcY, &dstRP, dstX, dstY, |
sizeX, sizeY, minterm); |
sizeX, sizeY, minterm); |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
The arguments here are the same as for BltBitMap() above. Note that the BltBitMapRastPort() function will respect the RastPort.Mask field. Only the planes specified in the Mask will be included in the operation. |
The arguments here are the same as for BltBitMap() above. Note that the BltBitMapRastPort() function will respect the RastPort.Mask field. Only the planes specified in the Mask will be included in the operation. |
||
Line 798: | Line 798: | ||
A third type of blitter operation is provided by the BltMaskBitMapRastPort() function. This works the same as BltBitMapRastPort() except that it takes one extra argument, a pointer to a single bitplane mask of the same height and width as the source. The mask acts as a filter for the operation–a blit only occurs where the mask plane is non-zero. The syntax for the function is: |
A third type of blitter operation is provided by the BltMaskBitMapRastPort() function. This works the same as BltBitMapRastPort() except that it takes one extra argument, a pointer to a single bitplane mask of the same height and width as the source. The mask acts as a filter for the operation–a blit only occurs where the mask plane is non-zero. The syntax for the function is: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | VOID BltMaskBitMapRastPort(& |
+ | VOID BltMaskBitMapRastPort(&srcBM, srcX, srcY, &dstRP, dstX, dstY, |
sizeX, sizeY, minterm, bltmask); |
sizeX, sizeY, minterm, bltmask); |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
The bltmask argument points to a word-aligned mask bitplane in Chip memory with the same dimensions as the source bitmap. Note that this function ignores the Mask field of the destination RastPort. |
The bltmask argument points to a word-aligned mask bitplane in Chip memory with the same dimensions as the source bitmap. Note that this function ignores the Mask field of the destination RastPort. |
||
Line 807: | Line 807: | ||
ClipBlit() takes most of the same arguments as the other blitter calls described above but it works with source and destination RastPorts and their layers. Before ClipBlit() moves data, it looks at the area from which and to which the data is being copied (RastPorts, not BitMaps) and determines if there are overlapping areas involved. If so, it splits up the overall operation into a number of bitmaps to move the data in the way you request. To call ClipBlit() use: |
ClipBlit() takes most of the same arguments as the other blitter calls described above but it works with source and destination RastPorts and their layers. Before ClipBlit() moves data, it looks at the area from which and to which the data is being copied (RastPorts, not BitMaps) and determines if there are overlapping areas involved. If so, it splits up the overall operation into a number of bitmaps to move the data in the way you request. To call ClipBlit() use: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | VOID ClipBlit(& |
+ | VOID ClipBlit(&srcRP, srcX, srcY, &dstRP, dstX, dstY, XSize, YSize, minterm); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
Since ClipBlit() respects the Layer of the source and destination RastPort, it is the easiest blitter movement call to use with Intuition windows. The following code fragments show how to save and restore an undo buffer using ClipBlit(). |
Since ClipBlit() respects the Layer of the source and destination RastPort, it is the easiest blitter movement call to use with Intuition windows. The following code fragments show how to save and restore an undo buffer using ClipBlit(). |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
/* Save work rastport to an undo rastport */ |
/* Save work rastport to an undo rastport */ |
||
− | ClipBlit(& |
+ | ClipBlit(&drawRP, 0, 0, &undoRP, 0, 0, areaWidth, areaHeight, 0xC0); |
/* restore undo rastport to work rastport */ |
/* restore undo rastport to work rastport */ |
||
− | ClipBlit(& |
+ | ClipBlit(&undoRP, 0, 0, &drawRP, 0, 0, areaWidth, areaHeight, 0xC0); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
==== Scaling Rectangular Areas ==== |
==== Scaling Rectangular Areas ==== |
||
− | BitMapScale() will scale a single bitmap any integral size up to 16,383 times its original size |
+ | BitMapScale() will scale a single bitmap any integral size up to 16,383 times its original size. It is called with the address of a BitScaleArgs structure (see <graphics/scale.h>). |
+ | <syntaxhighlight> |
||
− | <pre> |
||
VOID BitMapScale(struct BitScaleArgs *bsa); |
VOID BitMapScale(struct BitScaleArgs *bsa); |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
The bsa argument specifies the BitMaps to use, the source and destination rectangles, as well as the scaling factor. The source and destination may ''not'' overlap. The caller must ensure that the destination BitMap is large enough to receive the scaled-up copy of the source rectangle. The function ScalerDiv() is provided to help in the calculation of the destination BitMap’s size. |
The bsa argument specifies the BitMaps to use, the source and destination rectangles, as well as the scaling factor. The source and destination may ''not'' overlap. The caller must ensure that the destination BitMap is large enough to receive the scaled-up copy of the source rectangle. The function ScalerDiv() is provided to help in the calculation of the destination BitMap’s size. |
||
Line 837: | Line 837: | ||
Internally, these graphics library functions operate in a loop, doing graphic operations with the blitter one plane at a time as follows: |
Internally, these graphics library functions operate in a loop, doing graphic operations with the blitter one plane at a time as follows: |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
− | OwnBlitter(); /* Gain exclusive access to the hardware blitter */ |
+ | IGraphics->OwnBlitter(); /* Gain exclusive access to the hardware blitter */ |
− | for(planes=0; planes |
+ | for(planes=0; planes < bitmap->depth; planes++) |
+ | { |
||
− | { |
||
− | + | IGraphics->WaitBlit(); /* Sleep until the previous blitter operation completes */ |
|
− | + | /* start a blit */ |
|
+ | } |
||
− | } |
||
− | DisownBlitter(); /* Release exclusive access to the hardware blitter */ |
+ | IGraphics->DisownBlitter(); /* Release exclusive access to the hardware blitter */ |
+ | </syntaxhighlight> |
||
− | </pre> |
||
Graphics library functions that are implemented this way always wait for the blitter at the start and exit right after the final blit is ''started''. It is important to note that when these blitter-using functions return to your task, the final (or perhaps only) blit has just been ''started'', but not necessarily ''completed''. This is efficient if you are making many such calls in a row because the next graphics blitter call always waits for the previous blitter operation to complete before starting its first blit. |
Graphics library functions that are implemented this way always wait for the blitter at the start and exit right after the final blit is ''started''. It is important to note that when these blitter-using functions return to your task, the final (or perhaps only) blit has just been ''started'', but not necessarily ''completed''. This is efficient if you are making many such calls in a row because the next graphics blitter call always waits for the previous blitter operation to complete before starting its first blit. |
||
Line 855: | Line 855: | ||
{{Note|title=Warning|text=If you do not follow the above procedure, you could end up with a program that works correctly most of the time but crashes sometimes. Or you may run into problems when your program is run on faster machines or under other circumstances where the blitter is not as fast as the processor.}} |
{{Note|title=Warning|text=If you do not follow the above procedure, you could end up with a program that works correctly most of the time but crashes sometimes. Or you may run into problems when your program is run on faster machines or under other circumstances where the blitter is not as fast as the processor.}} |
||
− | ==== Accessing the Blitter Directly ==== |
||
− | |||
− | To use the blitter directly, you must first be familiar with how its registers control its operation. This topic is covered thoroughly in the ''Amiga Hardware Reference Manual'' and is not repeated here. There are two basic approaches you can take to perform direct programming of the blitter: synchronous and asynchronous. |
||
− | |||
− | * Synchronous programming of the blitter is used when you want to do a job with the blitter right away. For synchronous programming, you first get exclusive access to the blitter with OwnBlitter(). Next call WaitBlit() to ensure that any previous blitter operation that might have been in progress is completed. Then set up your blitter operation by programming the blitter registers. Finally, start the blit and call DisownBlitter(). |
||
− | |||
− | * Asynchronous programming of the blitter is used when the blitter operation you want to perform does not have to happen immediately. In that case, you can use the QBlit() and QBSBlit() functions in order to queue up requests for the use of the blitter on a non-exclusive basis. You share the blitter with system tasks. |
||
− | |||
− | Whichever approach you take, there is one rule you should generally keep in mind about using the blitter directly: |
||
− | |||
− | {{Note|title=Don’t Tie Up The Blitter|text=The system uses the blitter extensively for disk and display operation. While your task is using the blitter, many other system processes will be locked out. Therefore, use it only for brief periods and relinquish it as quickly as possible.}} |
||
− | |||
− | To use QBlit() and QBSBlit(), you must create a data structure called a bltnode (blitter node) that contains a pointer to the blitter code you want to execute. The system uses this structure to link blitter usage requests into a first-in, first-out (FIFO) queue. When your turn comes, your own blitter routine can be repeatedly called until your routine says it is finished using the blitter. |
||
− | |||
− | Two separate blitter queues are maintained. One queue is for the QBlit() routine. You use QBlit() when you simply want something done and you do not necessarily care when it happens. This may be the case when you are moving data in a memory area that is not currently being displayed. |
||
− | |||
− | The second queue is maintained for QBSBlit(). QBS stands for “queue-beam-synchronized”. QBSBlit() requests form a beam-synchronized FIFO queue. When the video beam gets to a predetermined position, your blitter routine is called. Beam synchronization takes precedence over the simple FIFO. This means that if the beam sync matches, the beam-synchronous blit will be done before the non-synchronous blit in the first position in the queue. You might use QBSBlit() to draw into an area of memory that is currently being displayed to modify memory that has already been “passed-over” by the video beam. This avoids display flicker as an area is being updated. |
||
− | |||
− | The sole input to both QBlit() and QBSBlit() is a pointer to a bltnode data structure, defined in the include file <hardware/blit.h>. Here is a copy of the structure, followed by details about the items you must initialize: |
||
− | |||
− | <pre> |
||
− | struct bltnode |
||
− | { |
||
− | struct bltnode *n; |
||
− | int (*function)(); |
||
− | char stat; |
||
− | short blitsize; |
||
− | short beamsync; |
||
− | int (*cleanup)(); |
||
− | }; |
||
− | </pre> |
||
− | |||
− | This is a pointer to the next bltnode, which, for most applications will be zero. You should not link bltnodes together. This is to be performed by the system in a separate call to QBlit() or QBSBlit(). |
||
− | |||
− | This is the address of your blitter function that the blitter queuer will call when your turn comes up. Your function must be formed as a subroutine, with an RTS instruction at the end. Follow Amiga programming conventions by placing the return value in D0 (or in C, use return(value)). |
||
− | |||
− | If you return a nonzero value, the system will call your routine again next time the blitter is idle until you finally return 0. This is done so that you can maintain control over the blitter; for example, it allows you to handle all five bitplanes if you are blitting an object with 32 colors. For display purposes, if you are blitting multiple objects and then saving and restoring the background, you must be sure that all planes of the object are positioned before another object is overlaid. This is the reason for the lockup in the blitter queue; it allows all work per object to be completed before going on to the next one. |
||
− | |||
− | {{Note|text=Not all C compilers can handle (*function)() properly! The system actually tests the processor ''status codes'' for a condition of equal-to-zero (Z flag set) or not-equal-to-zero (Z flag clear) when your blitter routine returns. Some C compilers do not set the processor status code properly (i.e., according to the value returned), thus it is not possible to use such compilers to write the (*function)() routine. In that case assembly language should be used. Blitter functions are normally written in assembly language anyway so they can take advantage of the ability of QBlit() and QBSBlit() to pass them parameters in processor registers.}} |
||
− | |||
− | The register passing conventions for these routines are as follows. Register A0 receives a pointer to the system hardware registers so that all hardware registers can be referenced as an offset from that address. Register A1 contains a pointer to the current bltnode. You may have queued up multiple blits, each of which perhaps uses the same blitter routine. You can access the data for this particular operation as an offset from the value in A1. For instance, a typical user of these routines can precalculate the blitter register values to be placed in the blitter registers and, when the routine is called, simply copy them in. For example, you can create a new structure such as the following: |
||
− | |||
− | <pre> |
||
− | INCLUDE "exec/types.i" |
||
− | INCLUDE "hardware/blit.i" |
||
− | |||
− | STRUCTURE mybltnode,0 |
||
− | ; Make this new structure compatible with a bltnode |
||
− | ; by making the first element a bltnode structure. |
||
− | STRUCT bltnode,bn_SIZEOF |
||
− | UWORD bltcon1 ; Blitter control register 1. |
||
− | UWORD fwmask ; First and last word masks. |
||
− | UWORD lwmask |
||
− | UWORD bltmda ; Modulos for sources a, b,and c. |
||
− | UWORD bltmdb |
||
− | UWORD bltmdc |
||
− | UWORD any_more_data ; add anything else you want |
||
− | LABEL mbn_SIZEOF |
||
− | </pre> |
||
− | |||
− | Other forms of data structures are certainly possible, but this should give you the general idea. |
||
− | |||
− | Tells the system whether or not to execute the clean-up routine at the end. This byte should be set to CLEANUP (0x40) if cleanup is to be performed. If not, then the bltnode cleanup variable can be zero. |
||
− | |||
− | The value that should be in the VBEAM counter for use during a beam-synchronous blit before the function() is called. |
||
− | |||
− | The system cooperates with you in planning when to start a blit in the routine QBSBlit() by not calling your routine until, for example, the video beam has already passed by the area on the screen into which you are writing. This is especially useful during single buffering of your displays. There may be time enough to write the object between scans of the video display. You will not be visibly writing while the beam is trying to scan the object. This avoids flicker (part of an old view of an object along with part of a new view of the object). |
||
− | |||
− | The address of a routine that is to be called after your last return from the QBlit() routine. When you finally return a zero, the queuer will call this subroutine (ends in RTS or return()) as the clean-up. Your first entry to the function may have dynamically allocated some memory or may have done something that must be undone to make for a clean exit. This routine must be specified. |
||
− | |||
− | == User Copper Lists == |
||
− | |||
− | The Copper coprocessor allows you to produce mid-screen changes in certain hardware registers in addition to changes that the system software already provides. For example, it is the Copper that allows the Amiga to split the viewing area into multiple draggable screens, each with its own independent set of colors. |
||
− | |||
− | To create your own mid-screen effects on the system hardware registers, you provide “user Copper lists” that can be merged into the system Copper lists. |
||
− | |||
− | In the ViewPort data structure there is a pointer named UCopIns. If this pointer value is non-NULL, it points to a user Copper list that you have dynamically allocated and initialized to contain your own special hardware-stuffing instructions. |
||
− | |||
− | You allocate a user Copper list by an instruction sequence such as the following: |
||
− | |||
− | <pre>struct UCopList *uCopList = NULL; |
||
− | |||
− | /* Allocate memory for the Copper list. Make certain that the initial */ |
||
− | /* memory is cleared. */ |
||
− | uCopList = (struct UCopList *) |
||
− | AllocMem(sizeof(struct UCopList), MEMF_PUBLIC|MEMF_CLEAR); |
||
− | |||
− | if (uCopList == NULL) |
||
− | return(FALSE);</pre> |
||
− | <sub>b</sub>oxNote:User Copper lists do ''not'' have to be in Chip RAM. |
||
− | |||
− | === Copper List Macros === |
||
− | |||
− | Once this pointer to a user Copper list is available, you can use it with system macros (<graphics/gfxmacros.h>) to instruct the system what to add to its own list of things for the Copper to do within a specific ViewPort. The file <graphics/gfxmacros.h> provides the following five macro functions that implement user Copper instructions. |
||
− | |||
− | initializes the Copper list buffer. It is used to specify how many instructions are going to be placed in the Copper list. It is called as follows. |
||
− | |||
− | CINIT(uCopList, num_entries); |
||
− | |||
− | The uCopList argument is a pointer tot he user Copper list and num_entries is the number of entries in the list. |
||
− | |||
− | waits for the video beam to reach a particular horizontal and vertical position. Its format is: |
||
− | |||
− | CWAIT(uCopList, v, h) |
||
− | |||
− | Again, uCopList is the pointer to the Copper list. The v argument is the vertical position for which to wait, specified relative to the top of the ViewPort. The legal range of values (for both NTSC and PAL) is from 0 to 255; h is the horizontal position for which to wait. The legal range of values (for both NTSC and PAL) is from 0 to 226. |
||
− | |||
− | installs a particular value into a specified system register. Its format is: |
||
− | |||
− | CMOVE(uCopList, reg, value) |
||
− | |||
− | Again, uCopList is the pointer to the Copper list. The reg argument is the register to be affected, specified in this form: custom.''register-name'' where the ''register-name'' is one of the registers listed in the Custom structure in <hardware/custom.h>. The value argument to CMOVE is the value to place in the register. |
||
− | |||
− | increments the user Copper list pointer to the next position in the list. It is usually invoked for the programmer as part of the macro definitions CWAIT or CMOVE. Its format is: |
||
− | |||
− | CBump(uCopList) |
||
− | |||
− | where uCopList is the pointer to the user Copper list. |
||
− | |||
− | terminates the user Copper list. Its format is: |
||
− | |||
− | CEND(uCopList) |
||
− | |||
− | where uCopList is the pointer to the user Copper list. |
||
− | |||
− | Executing any of the user Copper list macros causes the system to dynamically allocate special data structures called intermediate Copper lists that are linked into your user Copper list (the list to which uCopList points) describing the operation. When you call the function MrgCop(&view) as shown in the section called “Forming A Basic Display,” the system uses all of its intermediate Copper lists to sort and merge together the real Copper lists for the system (LOFCprList and SHFCprList). |
||
− | |||
− | When your program exits, you must return to the system all of the memory that you allocated or caused to be allocated. This means that you must return the intermediate Copper lists, as well as the user Copper list data structure. Here are two different methods for returning this memory to the system. |
||
− | |||
− | <pre>/* Returning memory to the system if you have NOT |
||
− | * obtained the ViewPort from Intuition. */ |
||
− | FreeVPortCopLists(viewPort); |
||
− | |||
− | /* Returning memory to the system if you HAVE |
||
− | * obtained the ViewPort from Intuition. */ |
||
− | CloseScreen(screen); /* Intuition only */</pre> |
||
− | |||
− | |||
− | User Copper lists may be clipped, under Release 2 and later, to ViewPort boundaries if the appropriate tag (VTAG_USERCLIP_SET) is passed to VideoControl(). Under earlier releases, the user Copper list would “leak” through to lower ViewPorts. |
||
− | |||
− | === Copper List Example === |
||
− | |||
− | The example program below shows the use of user Copper lists under Intuition. |
||
− | |||
− | |||
− | |||
− | <pre>/* UserCopperExample.c |
||
− | User Copper List Example |
||
− | For SAS/C 5.10a, |
||
− | compile with: LC -b1 -cfist -L -v -y UserCopperExample.c |
||
− | link with lc.lib and amiga.lib |
||
− | */ |
||
− | |||
− | #include <exec/types.h> |
||
− | #include <exec/memory.h> |
||
− | #include <graphics/gfxbase.h> |
||
− | #include <graphics/gfxmacros.h> |
||
− | #include <graphics/copper.h> |
||
− | #include <graphics/videocontrol.h> |
||
− | #include <intuition/intuition.h> |
||
− | #include <intuition/preferences.h> |
||
− | #include <hardware/custom.h> |
||
− | #include <libraries/dos.h> |
||
− | |||
− | #include <clib/exec_protos.h> /* Prototypes. */ |
||
− | #include <clib/graphics_protos.h> |
||
− | #include <clib/intuition_protos.h> |
||
− | #include <clib/dos_protos.h> |
||
− | |||
− | #include <stdlib.h> |
||
− | |||
− | /* Use this structure to gain access to the custom registers. */ |
||
− | extern struct Custom far custom; |
||
− | |||
− | /* Global variables. */ |
||
− | struct GfxBase *GfxBase = NULL; |
||
− | struct IntuitionBase *IntuitionBase = NULL; |
||
− | struct Screen *screen = NULL; |
||
− | struct Window *window = NULL; |
||
− | |||
− | VOID main( VOID ), cleanExit( WORD ); |
||
− | WORD openAll( VOID ), loadCopper( VOID ); |
||
− | |||
− | |||
− | /* |
||
− | * The main() routine -- just calls subroutines |
||
− | */ |
||
− | VOID main( VOID ) |
||
− | { |
||
− | WORD ret_val; |
||
− | struct IntuiMessage *intuiMessage; |
||
− | |||
− | /* Open the libraries, a screen and a window. */ |
||
− | ret_val = openAll(); |
||
− | if (RETURN_OK == ret_val) |
||
− | { |
||
− | /* Create and attach the user Copper list. */ |
||
− | ret_val = loadCopper(); |
||
− | if (RETURN_OK == ret_val) |
||
− | { |
||
− | /* Wait until the user clicks in the close gadget. */ |
||
− | (VOID) Wait(1<<window->UserPort->mp_SigBit); |
||
− | |||
− | while (intuiMessage = (struct IntuiMessage *)GetMsg(window->UserPort)) |
||
− | ReplyMsg((struct Message *)intuiMessage); |
||
− | } |
||
− | } |
||
− | cleanExit(ret_val); |
||
− | } |
||
− | |||
− | |||
− | /* |
||
− | * openAll() -- opens the libraries, screen and window |
||
− | */ |
||
− | WORD openAll( VOID ) |
||
− | { |
||
− | #define MY_WA_WIDTH 270 /* Width of window. */ |
||
− | |||
− | WORD ret_val = RETURN_OK; |
||
− | |||
− | /* Prepare to explicitly request Topaz 60 as the screen font. */ |
||
− | struct TextAttr topaz60 = |
||
− | { |
||
− | (STRPTR)"topaz.font", |
||
− | (UWORD)TOPAZ_SIXTY, (UBYTE)0, (UBYTE)0 |
||
− | }; |
||
− | |||
− | GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 37L); |
||
− | if (GfxBase == NULL) |
||
− | ret_val = ERROR_INVALID_RESIDENT_LIBRARY; |
||
− | else |
||
− | { |
||
− | IntuitionBase = (struct IntuitionBase *) |
||
− | OpenLibrary("intuition.library", 37L); |
||
− | |||
− | if (IntuitionBase == NULL) |
||
− | ret_val = ERROR_INVALID_RESIDENT_LIBRARY; |
||
− | else |
||
− | { |
||
− | screen = OpenScreenTags( NULL, |
||
− | SA_Overscan, OSCAN_STANDARD, |
||
− | SA_Title, "User Copper List Example", |
||
− | SA_Font, (ULONG)&topaz60, |
||
− | TAG_DONE); |
||
− | |||
− | if (NULL == screen) |
||
− | ret_val = ERROR_NO_FREE_STORE; |
||
− | else |
||
− | { |
||
− | window = OpenWindowTags( NULL, |
||
− | WA_CustomScreen, screen, |
||
− | WA_Title, "<- Click here to quit.", |
||
− | WA_IDCMP, CLOSEWINDOW, |
||
− | WA_Flags, WINDOWDRAG|WINDOWCLOSE|INACTIVEWINDOW, |
||
− | WA_Left, (screen->Width-MY_WA_WIDTH)/2, |
||
− | WA_Top, screen->Height/2, |
||
− | WA_Height, screen->Font->ta_YSize + 3, |
||
− | WA_Width, MY_WA_WIDTH, |
||
− | TAG_DONE); |
||
− | |||
− | if (NULL == window) |
||
− | ret_val = ERROR_NO_FREE_STORE; |
||
− | } |
||
− | } |
||
− | } |
||
− | |||
− | return(ret_val); |
||
− | } |
||
− | |||
− | |||
− | /* |
||
− | * loadCopper() -- creates a Copper list program and adds it to the system |
||
− | */ |
||
− | WORD loadCopper( VOID ) |
||
− | { |
||
− | register USHORT i, scanlines_per_color; |
||
− | WORD ret_val = RETURN_OK; |
||
− | struct ViewPort *viewPort; |
||
− | struct UCopList *uCopList = NULL; |
||
− | struct TagItem uCopTags[] = |
||
− | { |
||
− | { VTAG_USERCLIP_SET, NULL }, |
||
− | { VTAG_END_CM, NULL } |
||
− | }; |
||
− | |||
− | UWORD spectrum[] = |
||
− | { |
||
− | 0x0604, 0x0605, 0x0606, 0x0607, 0x0617, 0x0618, 0x0619, |
||
− | 0x0629, 0x072a, 0x073b, 0x074b, 0x074c, 0x075d, 0x076e, |
||
− | 0x077e, 0x088f, 0x07af, 0x06cf, 0x05ff, 0x04fb, 0x04f7, |
||
− | 0x03f3, 0x07f2, 0x0bf1, 0x0ff0, 0x0fc0, 0x0ea0, 0x0e80, |
||
− | 0x0e60, 0x0d40, 0x0d20, 0x0d00 |
||
− | }; |
||
− | |||
− | #define NUMCOLORS 32 |
||
− | |||
− | /* Allocate memory for the Copper list. */ |
||
− | /* Make certain that the initial memory is cleared. */ |
||
− | uCopList = (struct UCopList *) |
||
− | AllocMem(sizeof(struct UCopList), MEMF_PUBLIC|MEMF_CLEAR); |
||
− | |||
− | if (NULL == uCopList) |
||
− | ret_val = ERROR_NO_FREE_STORE; |
||
− | else |
||
− | { |
||
− | /* Initialize the Copper list buffer. */ |
||
− | CINIT(uCopList, NUMCOLORS); |
||
− | |||
− | scanlines_per_color = screen->Height/NUMCOLORS; |
||
− | |||
− | /* Load in each color. */ |
||
− | for (i=0; i<NUMCOLORS; i++) |
||
− | { |
||
− | CWAIT(uCopList, (i*scanlines_per_color), 0); |
||
− | CMOVE(uCopList, custom.color[0], spectrum[i]); |
||
− | } |
||
− | |||
− | CEND(uCopList); /* End the Copper list */ |
||
− | |||
− | viewPort = ViewPortAddress(window); /* Get a pointer to the ViewPort. */ |
||
− | Forbid(); /* Forbid task switching while changing the Copper list. */ |
||
− | viewPort->UCopIns=uCopList; |
||
− | Permit(); /* Permit task switching again. */ |
||
− | |||
− | /* Enable user copper list clipping for this ViewPort. */ |
||
− | (VOID) VideoControl( viewPort->ColorMap, uCopTags ); |
||
− | |||
− | RethinkDisplay(); /* Display the new Copper list. */ |
||
− | |||
− | return(ret_val); |
||
− | } |
||
− | } |
||
− | |||
− | |||
− | /* |
||
− | * cleanExit() -- returns all resources that were used. |
||
− | */ |
||
− | VOID cleanExit( WORD retval ) |
||
− | { |
||
− | struct ViewPort *viewPort; |
||
− | |||
− | if (NULL != IntuitionBase) |
||
− | { |
||
− | if (NULL != screen) |
||
− | { |
||
− | if (NULL != window) |
||
− | { |
||
− | viewPort = ViewPortAddress(window); |
||
− | if (NULL != viewPort->UCopIns) |
||
− | { |
||
− | /* Free the memory allocated for the Copper. */ |
||
− | FreeVPortCopLists(viewPort); |
||
− | RemakeDisplay(); |
||
− | } |
||
− | CloseWindow(window); |
||
− | } |
||
− | CloseScreen(screen); |
||
− | } |
||
− | CloseLibrary((struct Library *)IntuitionBase); |
||
− | } |
||
− | |||
− | if (NULL != GfxBase) |
||
− | CloseLibrary((struct Library *)GfxBase); |
||
− | |||
− | exit((int)retval); |
||
− | }</pre> |
||
− | == ECS and Genlocking Features == |
||
− | |||
− | |||
− | |||
− | The Enhanced Chip Set (ECS) Denise chip (''8373-R2a''), coupled with the Release 2 graphics library, opens up a whole new set of genlocking possibilities. Unlike the old Denise, whose only genlocking ability allowed keying on color register zero, the ECS Denise allows keying on any color register. Also, the ECS Denise allows keying on any bitplane of the ViewPort being genlocked. With the ECS Denise, the border area surrounding the display can be made transparent (always passes video) or opaque (overlays using color 0). All the new features are set individually for each ViewPort. These features can be used in conjunction with each other, making interesting scenarios possible. |
||
− | |||
− | === Genlock Control === |
||
− | |||
− | Using VideoControl(), a program can enable, disable, or obtain the state of a ViewPort’s genlocking features. It returns NULL if no error occurred. The function uses a tag based interface: |
||
− | |||
− | <pre>error = BOOL VideoControl( struct ColorMap *cm, struct TagItem *ti );</pre> |
||
− | The ti argument is a list of video commands stored in an array of TagItem structures. The cm argument specifies which ColorMap and, indirectly, which ViewPort these genlock commands will be applied to. The possible commands are: |
||
− | |||
− | <pre>VTAG_BITPLANEKEY_GET, _SET, _CLR |
||
− | VTAG_CHROMA_PLANE_GET, _SET |
||
− | VTAG_BORDERBLANK_GET, _SET, _CLR |
||
− | VTAG_BORDERNOTRANS_GET, _SET, _CLR |
||
− | VTAG_CHROMAKEY_GET, _SET, _CLR |
||
− | VTAG_CHROMAPEN_GET, _SET, _CLR</pre> |
||
− | This section covers only the genlock VideoControl() tags. See <graphics/videocontrol.h> for a complete list of all the available tags you can use with VideoControl(). |
||
− | |||
− | VTAG_BITPLANEKEY_GET |
||
− | |||
− | is used to find out the status of the bitplane keying mode. VTAG_BITPLANEKEY_SET and VTAG_BITPLANEKEY_CLR activate and deactivate bitplane keying mode. If bitplane key mode is on, genlocking will key on the bits set in a specific bitplane from the ViewPort (the specific bitplane is set with a different tag). The data portion of these tags is NULL. |
||
− | |||
− | For inquiry commands like VTAG_BITPLANEKEY_GET (tags ending in _GET), VideoControl() changes the _GET tag ID (ti_Tag) to the corresponding _SET or _CLR tag ID, reflecting the current state of the genlock mode. For example, when passed the following tag array: |
||
− | |||
− | <pre>struct TagItem videocommands[] = |
||
− | { |
||
− | {VTAG_BITPLANEKEY_GET, NULL}, |
||
− | {VTAG_END_CM, NULL} |
||
− | };</pre> |
||
− | VideoControl() |
||
− | |||
− | changes the VTAG_BITPLANEKEY_GET tag ID (ti_Tag) to VTAG_BITPLANEKEY_SET if bitplane keying is currently on, or to VTAG_BITPLANEKEY_CLR if bitplane keying is off. In both of these cases, VideoControl() only uses the tag’s ID, ignoring the tag’s data field (ti_Data). |
||
− | |||
− | The VTAG_CHROMA_PLANE_GET tag returns the number of the bitplane keyed on when bitplane keying mode is on. VideoControl() changes the tag’s data value to the bitplane number. VTAG_CHROMA_PLANE_SET sets the bitplane number to the tag’s data value. |
||
− | |||
− | VTAG_BORDERBLANK_GET |
||
− | |||
− | is used to obtain the border blank mode status. This tag works exactly like VTAG_BITPLANEKEY_GET. VideoControl() changes the tag’s ID to reflect the current border blanking state. VTAG_BORDERBLANK_SET and VTAG_BORDERBLANK_CLR activate and deactivate border blanking. If border blanking is on, the Amiga will not display anything in its display border, allowing an external video signal to show through the border area. On the Amiga display, the border appears black. The data portion of these tags is NULL. |
||
− | |||
− | The VTAG_BORDERNOTRANS_GET, _SET and _CLR tags are used, respectively, to obtain the status of border-not-transparent mode, and to activate and to deactivate this mode. If set, the Amiga display’s border will overlay external video with the color in register 0. Because border blanking mode takes precedence over border-not-transparent mode, setting border-not-transparent has no effect if border blanking is on. The data portion of these tags is NULL. |
||
− | |||
− | The VTAG_CHROMAKEY_GET, _SET and _CLR tags are used, respectively, to obtain the status of chroma keying mode, and to activate and deactivate chroma keying mode. If set, the genlock will key on colors from specific color registers (the specific color registers are set using a different tag). If chroma keying is not set, the genlock will key on color register 0. The data portion of these tags is NULL. |
||
− | |||
− | VTAG_CHROMAPEN_GET |
||
− | |||
− | obtains the chroma keying status of an individual color register. The tag’s ti_Data field contains the register number. Like the other _GET tags, VideoControl() changes the tag ID (ti_Tag) to one that reflects the current state of the mode. VTAG_CHROMAPEN_SET and VTAG_CHROMAPEN_CLR activate and deactivate chroma keying for each individual color register. Chroma keying can be active for more than one register. By turning off border blanking and activating chroma keying mode, but turning off chroma keying for each color register, a program can overlay every part of an external video source, completely blocking it out. |
||
− | |||
− | After using VideoControl() to set values in the ColorMap, the corresponding ViewPort has to be rebuilt with MakeVPort(), MrgCop() and LoadView(), so the changes can take effect. A program that uses a screen’s ViewPort rather than its own ViewPort should use the Intuition functions MakeScreen() and RethinkDisplay() to make the display changes take effect. |
||
− | |||
− | The following code fragment shows how to access the genlock modes. |
||
− | |||
− | <pre>struct Screen *genscreen; |
||
− | struct ViewPort *vp; |
||
− | struct TagItem vtags [24]; |
||
− | |||
− | /* The complete example opened a window, rendered some colorbars, */ |
||
− | /* and added gadgets to allow the user to turn the various genlock */ |
||
− | /* modes on and off. */ |
||
− | |||
− | vp = &(genscreen->ViewPort); |
||
− | |||
− | /* Ascertain the current state of the various modes. */ |
||
− | |||
− | /* Is borderblanking on? */ |
||
− | vtags[0].ti_Tag = VTAG_BORDERBLANK_GET; |
||
− | vtags[0].ti_Data = NULL; |
||
− | |||
− | /* Is bordertransparent set? */ |
||
− | vtags[1].ti_Tag = VTAG_BORDERNOTRANS_GET; |
||
− | vtags[1].ti_Data = NULL; |
||
− | |||
− | /* Key on bitplane? */ |
||
− | vtags[2].ti_Tag = VTAG_BITPLANEKEY_GET; |
||
− | vtags[2].ti_Tag = NULL; |
||
− | |||
− | /* Get plane which is used to key on */ |
||
− | vtags[3].ti_Tag = VTAG_CHROMA_PLANE_GET; |
||
− | vtags[3].ti_Data = NULL; |
||
− | |||
− | /* Chromakey overlay on? */ |
||
− | vtags[4].ti_Tag = VTAG_CHROMAKEY_GET; |
||
− | vtags[4].ti_Data = NULL; |
||
− | |||
− | for (i = 0; i < 16; i++) |
||
− | { |
||
− | /* Find out which colors overlay */ |
||
− | vtags[i + 5].ti_Tag = VTAG_CHROMA_PEN_GET; |
||
− | vtags[i + 5].ti_Data = i; |
||
− | } |
||
− | |||
− | /* Indicate end of tag array */ |
||
− | vtags[21].ti_Tag = VTAG_END_CM; |
||
− | vtags[21].ti_Data = NULL; |
||
− | |||
− | /* And send the commands. On return the Tags themselves will |
||
− | * indicate the genlock settings for this ViewPort's ColorMap. |
||
− | */ |
||
− | error = VideoControl(vp->ColorMap, vtags); |
||
− | |||
− | /* The complete program sets gadgets to reflect current states. */ |
||
− | |||
− | /* Will only send single commands from here on. */ |
||
− | vtags[1].ti_Tag = VTAG_END_CM; |
||
− | |||
− | /* At this point the complete program gets an input event and sets/clears the |
||
− | genlock modes as requested using the vtag list and VideoControl(). |
||
− | */ |
||
− | |||
− | /* send video command */ |
||
− | error = VideoControl(vp->ColorMap, vtags); |
||
− | |||
− | /* Now use MakeScreen() and RethinkDisplay() to make the VideoControl() |
||
− | * changes take effect. If we were using our own ViewPort rather than |
||
− | * borrowing one from a screen, we would instead do: |
||
− | * |
||
− | * MakeVPort(ViewAddress(),vp); |
||
− | * MrgCop(ViewAddress()); |
||
− | * LoadView(ViewAddres()); |
||
− | */ |
||
− | MakeScreen(genscreen); |
||
− | RethinkDisplay(); |
||
− | |||
− | /* The complete program closes and frees everything it had opened or allocated. */ |
||
− | |||
− | |||
− | /* The complete example calls the CheckPAL function, which is included below in its |
||
− | entirety for illustrative purposes. |
||
− | */ |
||
− | |||
− | BOOL CheckPAL(STRPTR screenname) |
||
− | { |
||
− | struct Screen *screen; |
||
− | ULONG modeID = LORES_KEY; |
||
− | struct DisplayInfo displayinfo; |
||
− | BOOL IsPAL; |
||
− | |||
− | if (GfxBase->LibNode.lib_Version >= 36) |
||
− | { |
||
− | /* |
||
− | * We got at least V36, so lets use the new calls to find out what |
||
− | * kind of videomode the user (hopefully) prefers. |
||
− | */ |
||
− | |||
− | if (screen = LockPubScreen(screenname)) |
||
− | { |
||
− | /* |
||
− | * Use graphics.library/GetVPModeID() to get the ModeID of the specified screen. |
||
− | * Will use the default public screen (Workbench most of the time) if NULL It is |
||
− | * _very_ unlikely that this would be invalid, heck it's impossible. |
||
− | */ |
||
− | if ((modeID = GetVPModeID(&(screen->ViewPort))) != INVALID_ID) |
||
− | { |
||
− | /* |
||
− | * If the screen is in VGA mode, we can't tell whether the system is PAL |
||
− | * or NTSC. So to be foolproof we fall back to the displayinfo of the default |
||
− | * monitor by inquiring about just the LORES_KEY displaymode if we don't know. |
||
− | * The default.monitor reflects the initial video setup of the system, thus |
||
− | * for either ntsc.monitor or pal.monitor. We only use the displaymode of the |
||
− | * is an alias specified public screen if it's display mode is PAL or NTSC and |
||
− | * NOT the default. |
||
− | */ |
||
− | if (!((modeID & MONITOR_ID_MASK) == NTSC_MONITOR_ID || |
||
− | (modeID & MONITOR_ID_MASK) == PAL_MONITOR_ID)) |
||
− | modeID = LORES_KEY; |
||
− | } |
||
− | UnlockPubScreen(NULL, screen); |
||
− | } /* if fails modeID = LORES_KEY. Can't lock screen, so fall back on default monitor. */ |
||
− | |||
− | if (GetDisplayInfoData(NULL, (UBYTE *) & displayinfo, |
||
− | sizeof(struct DisplayInfo), DTAG_DISP, modeID)) |
||
− | { |
||
− | if (displayinfo.PropertyFlags & DIPF_IS_PAL) |
||
− | IsPAL = TRUE; |
||
− | else |
||
− | IsPAL = FALSE; |
||
− | /* Currently the default monitor is always either PAL or NTSC. */ |
||
− | } |
||
− | } |
||
− | else |
||
− | /* < V36. The enhancements to the videosystem in V36 (and above) cannot be better |
||
− | * expressed than with the simple way to determine PAL in V34. |
||
− | */ |
||
− | IsPAL= (GfxBase->DisplayFlags & PAL) ? TRUE : FALSE; |
||
− | |||
− | return(IsPAL); |
||
− | }</pre> |
||
== Function Reference == |
== Function Reference == |
||
Latest revision as of 18:52, 6 November 2015
This page is currently being updated to AmigaOS 4.x. Some of the information contained here may not yet be applicable in part or totally. |
Contents
- 1 Graphics Primitives
- 2 Drawing Routines
- 2.1 The RastPort Structure
- 2.1.1 Initializing a RastPort Structure
- 2.1.2 RastPort Area-fill Information
- 2.1.3 RastPort Graphics Element Pointer
- 2.1.4 RastPort Write Mask
- 2.1.5 RastPort Drawing Pens
- 2.1.6 RastPort Drawing Modes
- 2.1.7 RastPort Line and Area Drawing Patterns
- 2.1.8 RastPort Pen Position and Size
- 2.1.9 Text Attributes
- 2.1.10 Working with 32 bit colours on direct mapped screens
- 2.2 Using the Graphics Drawing Routines
- 2.2.1 Drawing Individual Pixels
- 2.2.2 Reading Individual Pixels
- 2.2.3 Drawing Ellipses and Circles
- 2.2.4 Drawing Lines
- 2.2.5 Drawing Patterned Lines
- 2.2.6 Drawing Multiple Lines with a Single Command
- 2.2.7 Area-fill Operations
- 2.2.8 Ellipse and Circle-fill Operations
- 2.2.9 Flood-fill Operations
- 2.2.10 Rectangle-fill Operations
- 2.3 Performing Data Move Operations
- 2.1 The RastPort Structure
- 3 Function Reference
Graphics Primitives
This article describes the basic graphics functions available to Amiga programmers. It covers the graphics support structures, display routines and drawing routines. Many of the operations described in this section are also performed by the Intuition software. See the Intuition articles for more information.
The Amiga supports several basic types of graphics routines: display routines, drawing routines, sprites and animation. These routines are very versatile and allow you to define any combination of drawing and display areas you may wish to use.
This section explains all of the available modes of drawing supported by the system software, including how to do the following:
- Reserve memory space for use by the drawing routines.
- Define the colors that can be drawn into a drawing area.
- Define the colors of the drawing pens (foreground pen, background pen for patterns, and outline pen for area-fill outlines).
- Define the pen position in the drawing area.
- Drawing primitives; lines, rectangles, circles and ellipses.
- Define vertex points for area-filling, and specify the area-fill color and pattern.
- Define a pattern for patterned line drawing.
- Change drawing modes.
- Read or write individual pixels in a drawing area.
- Copy rectangular blocks of drawing area data from one drawing area to another.
- Use a template (predefined shape) to draw an object into a drawing area.
For those working with Classic Amiga displays such as ECS and AGA you will want to reference Classic Graphics Primitives.
Drawing Routines
Most of the graphics drawing routines require information about how the drawing is to take place. For this reason, most graphics drawing routines use a data structure called a RastPort, that contains pointers to the drawing area and drawing variables such as the current pen color and font to use. In general, you pass a pointer to your RastPort structure as an argument whenever you call a drawing function.
The RastPort Structure
The RastPort data structure can be found in the include files <graphics/rastport.h> and <graphics/rastport.i>. It contains the following information:
struct RastPort { struct Layer *Layer; struct BitMap *BitMap; UWORD *AreaPtrn; /* Ptr to areafill pattern */ struct TmpRas *TmpRas; struct AreaInfo *AreaInfo; struct GelsInfo *GelsInfo; UBYTE Mask; /* Write mask for this raster */ BYTE FgPen; /* Foreground pen for this raster */ BYTE BgPen; /* Background pen */ BYTE AOlPen; /* Areafill outline pen */ BYTE DrawMode; /* Drawing mode for fill, lines, and text */ BYTE AreaPtSz; /* 2^n words for areafill pattern */ BYTE linpatcnt; /* Current line drawing pattern preshift */ BYTE dummy; UWORD Flags; /* Miscellaneous control bits */ UWORD LinePtrn; /* 16 bits for textured lines */ WORD cp_x, cp_y; /* Current pen position */ UBYTE minterms[8]; WORD PenWidth; WORD PenHeight; struct TextFont *Font; /* Current font address */ UBYTE AlgoStyle; /* The algorithmically generated style */ UBYTE TxFlags; /* Text specific flags */ UWORD TxHeight; /* Text height */ UWORD TxWidth; /* Text nominal width */ UWORD TxBaseline; /* Text baseline */ WORD TxSpacing; /* Text spacing (per character) */ APTR *RP_User; ULONG longreserved[2]; #ifndef GFX_RASTPORT_1_2 UWORD wordreserved[7]; /* Used to be a node */ UBYTE reserved[8]; /* For future use */ #endif };
The sections that follow explain each of the items in the RastPort structure is used.
Initializing a RastPort Structure
Once you have a BitMap set up, you can declare and initialize the RastPort and then link the BitMap into it. Here is a sample initialization sequence:
struct BitMap bitMap = {0}; struct RastPort rastPort = {0}; /* Initialize the RastPort and link the BitMap to it. */ InitRastPort(&rastPort); rastPort.BitMap = &bitMap;
Initialize, Then Link |
---|
You cannot link the bitmap in until after the RastPort has been initialized. |
RastPort Area-fill Information
Two structures in the RastPort–AreaInfo and TmpRas–define certain information for area filling operations. The AreaInfo pointer is initialized by a call to the routine InitArea().
#define AREA_SIZE 200 register USHORT i; WORD areaBuffer[AREA_SIZE]; struct AreaInfo areaInfo = {0}; /* Clear areaBuffer before calling InitArea(). */ for (i=0; i < AREA_SIZE; i++) areaBuffer[i] = 0; InitArea(&areaInfo, areaBuffer, (AREA_SIZE*2)/5);
The area buffer must start on a word boundary. That is why the sample declaration shows areaBuffer as composed of unsigned words (200), rather than unsigned bytes (400). It still reserves the same amount of space, but aligns the data space correctly.
To use area fill, you must first provide a work space in memory for the system to store the list of points that define your area. You must allow a storage space of 5 bytes per vertex. To create the areas in the work space, you use the functions AreaMove(), AreaDraw(), and AreaEnd().
Typically, you prepare the RastPort for area-filling by following the steps in the code fragment above and then linking your AreaInfo into the RastPort like so:
rastPort->AreaInfo = &areaInfo;
In addition to the AreaInfo structure in the RastPort, you must also provide the system with some work space to build the object whose vertices you are going to define. This requires that you initialize a TmpRas structure, then point to that structure for your RastPort to use.
Here is a code fragment that builds and initializes a TmpRas. First the TmpRas structure is initialized (via InitTmpRas()) then it is linked into the RastPort structure.
Allocate Enough Space |
---|
The area to which TmpRas.RasPtr points must be at least as large as the area (width times height) of the largest rectangular region you plan to fill. Typically, you allocate a space as large as a single bitplane (usually 320*200 bits for Lores mode, 640*200 for Hires, and 1,280*200 for SuperHires). |
When you use functions that dynamically allocate memory from the system, you must remember to return these memory blocks to the system before your program exits. See the description of FreeRaster() in the SDK.
RastPort Graphics Element Pointer
The graphics element pointer in the RastPort structure is called GelsInfo.
If you are doing graphics animation using the GELS system, this pointer must refer to a properly initialized GelsInfo structure. See Graphics Sprites, Bobs and Animation for more information.
RastPort Write Mask
The write mask is a RastPort variable that determines which of the bitplanes are currently writable.
For most applications, this variable is set to all bits on. This means that all bitplanes defined in the BitMap are affected by a graphics writing operation. You can selectively disable one or more bitplanes by simply specifying a 0 bit in that specific position in the control byte. For example:
uint32 result = IGraphics->SetWriteMask(&rastPort, 0xFB); /* disable bitplane 2 */ if (result == 0) IDOS->Printf("Can't set write mask\n");
A useful application for the Mask field is to set or clear plane 6 while in the Extra-Half-Brite display mode to create shadow effects. For example:
IGraphics->SetWriteMask(&rastPort, 0xE0); /* Disable planes 1 through 5. */ IGraphics->SetAPen(&rastPort, 0); /* Clear the Extra-Half-Brite bit */ IGraphics->RectFill(&rastPort, 20, 20, 40, 30); /* in the old rectangle. */ IGraphics->SetAPen(&rastPort, 32); /* Set the Extra-Half-Brite bit */ IGraphics->RectFill(&rastPort, 30, 25, 50, 35); /* in the new rectangle. */ IGraphics->SetWrMsk(&rastPort, -1); /* Re-enable all planes. */
Note |
---|
Do not use the obsolete SetWrMsk() macro in new code. |
RastPort Drawing Pens
The Amiga has three different drawing “pens” associated with the graphics drawing routines. These are:
- FgPen
- The foreground or primary drawing pen. For historical reasons, it is also called the A-Pen.
- BgPen
- The background or secondary drawing pen. For historical reasons, it is also called the B-Pen.
- AOlPen
- The area outline pen. For historical reasons, it is also called the O-Pen.
A drawing pen variable in the RastPort contains the current value (range 0-255) for a particular color choice. This value represents a color register number whose contents are to be used in rendering a particular type of image. The effect of the pen value is dependent upon the drawing mode and can be influenced by the pattern variables and the write mask as described below. Always use the system calls (e.g. SetAPen()) to set the different pens, never store values directly into the pen fields of the RastPort.
Colors Repeat Beyond 31 |
---|
The Amiga 500/2000/3000 (with original chips or ECS) contains only 32 color registers. Any range beyond that repeats the colors in 0-31. For example, pen numbers 32-63 refer to the colors in registers 0-31 (except when you are using Extra-Half-Brite mode). |
The graphics library drawing routines support BitMaps up to eight planes deep allowing for future expansion of the Amiga hardware.
The color in FgPen is used as the primary drawing color for rendering lines and areas. This pen is used when the drawing mode is JAM1 (see the next section for drawing modes). JAM1 specifies that only one color is to be “jammed” into the drawing area.
You establish the color for FgPen using the statement:
SetAPen(&rastPort, newcolor);
The color in BgPen is used as the secondary drawing color for rendering lines and areas. If you specify that the drawing mode is JAM2 (jamming two colors) and a pattern is being drawn, the primary drawing color (FgPen) is used where there are 1s in the pattern. The secondary drawing color (BgPen) is used where there are 0s in the pattern.
You establish the drawing color for BgPen using the statement:
SetBPen(&rastPort, newcolor);
The area outline pen AOlPen is used in two applications: area fill and flood fill. (See “Area Fill Operations” below.) In area fill, you can specify that an area, once filled, can be outlined in this AOlPen color. In flood fill (in one of its operating modes) you can fill until the flood-filler hits a pixel of the color specified in this pen variable.
You establish the drawing color for AOlPen using the statement:
SetOutlinePen(&rastPort, newcolor);
Note |
---|
Do not use the obsolete SetOPen() macro. |
RastPort Drawing Modes
Four drawing modes may be specified in the RastPort.DrawMode field:
- JAM1
- Whenever you execute a graphics drawing command, one color is jammed into the target drawing area. You use only the primary drawing pen color, and for each pixel drawn, you replace the color at that location with the FgPen color.
- JAM2
- Whenever you execute a graphics drawing command, two colors are jammed into the target drawing area. This mode tells the system that the pattern variables (both line pattern and area pattern – see the next section) are to be used for the drawing. Wherever there is a 1 bit in the pattern variable, the FgPen color replaces the color of the pixel at the drawing position. Wherever there is a 0 bit in the pattern variable, the BgPen color is used.
- COMPLEMENT
- For each 1 bit in the pattern, the corresponding bit in the target area is complemented – that is, its state is reversed. As with all other drawing modes, the write mask can be used to protect specific bitplanes from being modified. Complement mode is often used for drawing and then erasing lines.
- INVERSVID
- This is the drawing mode used primarily for text. If the drawing mode is (JAM1 | INVERSVID), the text appears as a transparent letter surrounded by the FgPen color. If the drawing mode is (JAM2|INVERSVID), the text appears as in (JAM1|INVERSVID) except that the BgPen color is used to draw the text character itself. In this mode, the roles of FgPen and BgPen are effectively reversed.
You set the drawing modes using the statement:
SetDrMd(&rastPort, newmode);
Set the newmode argument to one of the four drawing modes listed above.
RastPort Line and Area Drawing Patterns
The RastPort data structure provides two different pattern variables that it uses during the various drawing functions: a line pattern and an area pattern.
The line pattern is 16 bits wide and is applied to all lines. When you initialize a RastPort, this line pattern value is set to all 1s (hex FFFF), so that solid lines are drawn. You can also set this pattern to other values to draw dotted lines if you wish.
For example, you can establish a dotted line pattern with the graphics macro SetDrPt():
SetDrPt(&rastPort, 0xCCCC);
The second argument is a bit-pattern, 1100110011001100, to be applied to all lines drawn. If you draw multiple, connected lines, the pattern cleanly connects all the points.
The area pattern is also 16 bits wide and its height is some power of two. This means that you can define patterns in heights of 1, 2, 4, 8, 16, and so on. To tell the system how large a pattern you are providing, use the graphics macro SetAfPt():
SetAfPt(&rastPort, &areaPattern, power_of_two);
The &areaPattern argument is the address of the first word of the area pattern and power_of_two specifies how many words are in the pattern. For example:
uint16 ditherData[] = { 0x5555, 0xAAAA }; SetAfPt(&rastPort, ditherData, 1);
This example produces a small cross-hatch pattern, useful for shading. Because power_of_two is set to 1, the pattern height is 2 to the 1st, or 2 rows high.
To clear the area fill pattern, use:
SetAfPt(&rastPort, NULL, 0);
Pattern Positioning |
---|
The pattern is always positioned with respect to the upper left corner of the RastPort drawing area (the 0,0 coordinate). If you draw two rectangles whose edges are adjacent, the pattern will be continuous across the rectangle boundaries. |
The last example given produces a two-color pattern with one color where there are 1s and the other color where there are 0s in the pattern. A special mode allows you to develop a pattern having up to 256 colors. To create this effect, specify power_of_two as a negative value instead of a positive value. For instance, the following initialization establishes an 8-color checkerboard pattern where each square in the checkerboard has a different color.
uint16 areaPattern[3][8] = { /* plane 0 pattern */ { 0x0000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff }, /* plane 1 pattern */ { 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff }, /* plane 2 pattern */ { 0xff00, 0xff00, 0xff00, 0xff00, 0xff00, 0xff00, 0xff00, 0xff00 } }; SetAfPt(&rastPort, areaPattern, -3); /* when doing this, it is best to set three other parameters as follows: */ SetAPen(&rastPort, -1); SetBPen(&rastPort, 0); SetDrMd(&rastPort, JAM2);
If you use this multicolored pattern mode, you must provide as many planes of pattern data as there are planes in your BitMap.
RastPort Pen Position and Size
The graphics drawing routines keep the current position of the drawing pen in the RastPort fields cp_x and cp_y, for the horizontal and vertical positions, respectively.
The coordinate location 0,0 is in the upper left corner of the drawing area. The x value increases proceeding to the right; the y value increases proceeding toward the bottom of the drawing area.
The variables RastPort.PenWidth and RastPort.PenHeight are not currently implemented. These fields should not be read or written by applications.
Text Attributes
Text attributes and font information are stored in the RastPort fields Font, AlgoStyle, TxFlags, TxHeight, TxWidth, TxBaseline and TxSpacing. These are normally set by calls to the graphics font routines which are covered separately in Graphics Library and Text.
Working with 32 bit colours on direct mapped screens
As well as the individual functions for setting the various RastPort attributes (e.g. SetAPen(), SetBPen()) you can also use the function SetRPAttrs(). SetRPAttrs() also allows you to define multiple attributes in a single call using a tag list.
When working with 32 bit colour values use SetRPAttrs() with the tags RPTAG_APenColor, RPTAG_BPenColor and RPTAG_OPenColor. This of course only works on direct mapped bitmaps (e.g. 16bit or 32bit).
The colour format used by the three tags above is 0xAARRGGBB. The alpha part is currently ignored and should be set to 0xFF so that the correct alpha is used when alpha blending has been implemented.
The following code snippet might be used to render a light blue and yellow dotted line using 32bit colours.
IGraphics->SetRPAttrs(rp, RPTAG_APenColor, 0xFF9999FF, // light blue RPTAG_BPenColor, 0xFFFFFF00, // yellow RPTAG_DrMd, JAM2, // use two colours TAG_END); SetDrPt(rp, 0xFF00); // NB this one is a macro so no 'IGraphics' IGraphics->Move(rp, x1, y1); IGraphics->Draw(rp, x2, y2);
Using the Graphics Drawing Routines
This section shows you how to use the Amiga drawing routines. All of these routines work either on their own or along with the windowing system and layers library. For details about using the layers and windows, see Layers Library and Intuition Windows.
Use WaitBlit() |
---|
The graphics library rendering and data movement routines generally wait to get access to the blitter, start their blit, and then exit. Therefore, you must WaitBlit() after a graphics rendering or data movement call if you intend to immediately deallocate, examine, or perform order-dependent processor operations on the memory used in the call. |
As you read this section, keep in mind that to use the drawing routines, you need to pass them a pointer to a RastPort. You can define the RastPort directly, as shown in the sample program segments in preceding sections, or you can get a RastPort from your Window structure using code like the following:
struct Window *window; struct RastPort *rastPort; window = IIntuition->OpenWindow(&newWindow); /* You could use OpenWindowTags() instead. */ if (window) rastPort = window->RPort;
You can also get the RastPort from the Layer structure, if you are not using Intuition.
Drawing Individual Pixels
You can set a specific pixel to a desired color by using a statement like this:
int16 x, y; int32 result = IGraphics->WritePixel(&rastPort, x, y);
WritePixel() uses the primary drawing pen and changes the pixel at that x,y position to the desired color if the x,y coordinate falls within the boundaries of the RastPort. A value of 0 is returned if the write was successful; a value of -1 is returned if x,y was outside the range of the RastPort.
Reading Individual Pixels
You can determine the color of a specific pixel with a statement like this:
int16 x, y; int32 result = IGraphics->ReadPixel(&rastPort, x, y);
ReadPixel() returns the value of the pixel color selector at the specified x,y location. If the coordinates you specify are outside the range of your RastPort, this function returns a value of -1.
Drawing Ellipses and Circles
Two functions are associated with drawing ellipses: DrawCircle() and DrawEllipse(). DrawCircle(), a macro that calls DrawEllipse(), will draw a circle from the specified center point using the specified radius. This function is executed by the statement:
DrawCircle(&rastPort, center_x, center_y, radius);
Similarly, DrawEllipse() draws an ellipse with the specified radii from the specified center point:
DrawEllipse(&rastPort, center_x, center_y, horiz_r, vert_r);
Neither function performs clipping on a non-layered RastPort.
Drawing Lines
Two functions are associated with line drawing: Move() and Draw().
Move() simply moves the cursor to a new position. It is like picking up a drawing pen and placing it at a new location. This function is executed by the statement:
Move(&rastPort, x, y);
Draw() draws a line from the current x,y position to a new x,y position specified in the statement itself. The drawing pen is left at the new position. This is done by the statement:
Draw(&rastPort, x, y);
Draw() uses the pen color specified for FgPen.
Here is a sample sequence that draws a line from location (0,0) to (100,50).
IGraphics->SetAPen(&rastPort, COLOR1); /* Set A pen color. */ IGraphics->Move(&rastPort, 0, 0); /* Move to this location. */ IGraphics->Draw(&rastPort, 100, 50); /* Draw to a this location. */
Caution |
---|
If you attempt to draw a line outside the bounds of the BitMap, using the basic initialized RastPort, you may crash the system. You must either do your own software clipping to assure that the line is in range, or use the layers library. Software clipping means that you need to determine if the line will fall outside your BitMap before you draw it, and render only the part which falls inside the BitMap. |
Drawing Patterned Lines
To turn the example above into a patterned line draw, simply set a drawing pattern, such as:
SetDrPt(&rastPort, 0xAAAA);
Now all lines drawn appear as dotted lines (0xAAAA = 1010101010101010 in binary). To resume drawing solid lines, execute the statement:
SetDrPt(&rastPort, ~0);
Because ~0 is defined as all bits on (11...11) in binary.
Drawing Multiple Lines with a Single Command
You can use multiple Draw() statements to draw connected line figures.
If the shapes are all definable as interconnected, continuous lines, you can use a simpler function, called PolyDraw(). PolyDraw() takes a set of line endpoints and draws a shape using these points. You call PolyDraw() with the statement:
PolyDraw(&rastPort, count, arraypointer);
PolyDraw() reads the array of points and draws a line from the first pair of coordinates to the second, then a connecting line to each succeeding pair in the array until count points have been connected. This function uses the current drawing mode, pens, line pattern, and write mask specified in the target RastPort; for example, this fragment draws a rectangle, using the five defined pairs of x,y coordinates.
int16 linearray[] = { 3, 3, 15, 3, 15,15, 3,15, 3, 3 }; IGraphics->PolyDraw(&rastPort, 5, linearray);
Area-fill Operations
Assuming that you have properly initialized your RastPort structure to include a properly initialized AreaInfo, you can perform area fill by using the functions described in this section.
AreaMove() tells the system to begin a new polygon, closing off any other polygon that may already be in process by connecting the end-point of the previous polygon to its starting point.
AreaMove() is executed with the statement:
int32 result = IGraphics->AreaMove(&rastPort, x, y);
AreaMove() returns 0 if successful, -1 if there was no more space left in the vector list.
AreaDraw() tells the system to add a new vertex to a list that it is building. No drawing takes place until AreaEnd() is executed. AreaDraw is executed with the statement:
int32 result = IGraphics->AreaDraw(&rastPort, x, y);
AreaDraw() returns 0 if successful, -1 if there was no more space left in the vector list.
AreaEnd() tells the system to draw all of the defined shapes and fill them. When this function is executed, it obeys the drawing mode and uses the line pattern and area pattern specified in your RastPort to render the objects you have defined.
To fill an area, you do not have to AreaDraw() back to the first point before calling AreaEnd(). AreaEnd() automatically closes the polygon. AreaEnd() is executed with the following statement:
int32 result = AreaEnd(&rastPort);
AreaEnd() returns 0 if successful, -1 if there was an error.
To turn off the outline function, you have to set the RastPort Flags variable back to 0 with BNDRYOFF():
#include <graphics/gfxmacros.h> BNDRYOFF(&rastPort);
Otherwise, every subsequent area-fill or rectangle-fill operation will outline their rendering with the outline pen (AOlPen).
Ellipse and Circle-fill Operations
Two functions are associated with drawing filled ellipses: AreaCircle() and AreaEllipse(). AreaCircle() (a macro that calls AreaEllipse()) will draw a circle from the specified center point using the specified radius. This function is executed by the statement:
IGraphics->AreaCircle(&rastPort, center_x, center_y, radius);
Similarly, AreaEllipse() draws a filled ellipse with the specified radii from the specified center point:
IGraphics->AreaEllipse(&rastPort, center_x, center_y, horiz_r, vert_r);
Outlining with SetOutlinePen() is not currently supported by the AreaCircle() and AreaEllipse() routines.
Caution |
---|
If you attempt to fill an area outside the bounds of the BitMap, using the basic initialized RastPort, it may crash the system. You must either do your own software clipping to assure that the area is in range, or use the layers library. |
Flood-fill Operations
Flood fill is a technique for filling an arbitrary shape with a color. The Amiga flood-fill routines can use a plain color or do the fill using a combination of the drawing mode, FgPen, BgPen and the area pattern.
Flood-fill requires a TmpRas structure at least as large as the RastPort in which the flood-fill will be done. This is to ensure that even if the flood-filling operation “leaks”, it will not flow outside the TmpRas and corrupt another task’s memory.
You use the Flood() routine for flood fill. The syntax for this routine is as follows:
IGraphics->Flood(&rastPort, mode, x, y);
The rastPort argument specifies the RastPort you want to draw into. The x and y arguments specify the starting coordinate within the BitMap. The mode argument tells how to do the fill. There are two different modes for flood fill:
In outline mode, you specify an x,y coordinate, and from that point the system searches outward in all directions for a pixel whose color is the same as that specified in the area outline pen (AOlPen). All horizontally or vertically adjacent pixels not of that color are filled with a colored pattern or plain color. The fill stops at the outline color. Outline mode is selected when the mode argument to Flood() is set to a 0.
In color mode, you specify an x,y coordinate, and whatever pixel color is found at that position defines the area to be filled. The system searches for all horizontally or vertically adjacent pixels whose color is the same as this one and replaces them with the colored pattern or plain color. Color mode is selected when the mode argument to Flood() is set to a one.
The following sample program fragment creates and then flood-fills a triangular region. The overall effect is exactly the same as shown in the preceding area-fill example above, except that flood-fill is slightly slower than area-fill. Mode 0 (fill to a pixel that has the color of the outline pen) is used in the example.
int8 oldAPen; uint16 oldDrPt; struct RastPort *rastPort = Window->RPort; /* Save the old values of the foreground pen and draw pattern. */ oldAPen = rastPort->FgPen; oldDrPt = rastPort->LinePtrn; /* Use AreaOutline pen color for foreground pen. */ IGraphics->SetAPen(rastPort, rastPort->AOlPen); IGraphics->SetDrPt(rastPort, ~0); /* Insure a solid draw pattern. */ IGraphics->Move(rastPort, 0, 0); /* Using mode 0 to create a triangular shape */ IGraphics->Draw(rastPort, 0, 100); IGraphics->Draw(rastPort, 100, 100); IGraphics->Draw(rastPort, 0, 0); /* close it */ IGraphics->SetAPen(rastPort, oldAPen); /* Restore original foreground pen. */ IGraphics->Flood(rastPort, 0, 10, 50); /* Start Flood() inside triangle. */ SetDrPt(rastPort, oldDrPt); /* Restore original draw mode. */
This example saves the current FgPen value and draws the shape in the same color as AOlPen.
Then FgPen is restored to its original color so that FgPen, BgPen, DrawMode, and AreaPtrn can be used to define the fill within the outline.
Rectangle-fill Operations
The final fill function, RectFill(), is for filling rectangular areas. The form of this function follows:
RectFill(&rastPort, xmin, ymin, xmax, ymax);
As usual, the rastPort argument specifies the RastPort you want to draw into. The xmin and ymin arguments specify the upper left corner of the rectangle to be filled. The xmax and ymax arguments specify the lower right corner of the rectangle to be filled. Note that the variable xmax must be equal to or greater than xmin, and ymax must be equal to or greater than ymin.
Rectangle-fill uses FgPen, BgPen, AOlPen, DrawMode, AreaPtrn and Mask to fill the area you specify.
Remember that the fill can be multicolored as well as single- or two-colored. When the DrawMode is COMPLEMENT, it complements all bit planes, rather than only those planes in which the foreground is non-zero.
Performing Data Move Operations
The graphics library includes several routines that use the hardware blitter to handle the rectangularly organized data that you work with when doing raster-based graphics. These blitter routines do the following:
- Clear an entire segment of memory
- Set a raster to a specific color
- Scroll a subrectangle of a raster
- Draw a pattern "through a stencil"
- Extract a pattern from a bit-packed array and draw it into a raster
- Copy rectangular regions from one bitmap to another
- Control and utilize the hardware-based data mover, the blitter
The following sections cover these routines in detail.
Warning |
---|
The graphics library rendering and data movement routines generally wait to get access to the blitter, start their blit, and then exit without waiting for the blit to finish. Therefore, you must WaitBlit() after a graphics rendering or data movement call if you intend to immediately deallocate, examine, or perform order-dependent processor operations on the memory used in the call. |
Clearing a Memory Area
For memory that is accessible to the blitter (that is, internal Chip memory), the most efficient way to clear a range of memory is to use the blitter. You use the blitter to clear a block of memory with the statement:
BltClear(memblock, bytecount, flags);
The memblock argument is a pointer to the location of the first byte to be cleared and bytecount is the number of bytes to set to zero. In general the flags variable should be set to one to wait for the blitter operation to complete. Refer to the SDK for other details about the flag argument.
Setting a Whole Raster to a Color
You can preset a whole raster to a single color by using the function SetRast().
A call to this function takes the following form:
SetRast(&rastPort, pen);
As always, the &rastPort is a pointer to the RastPort you wish to use. Set the pen argument to the color register you want to fill the RastPort with.
Scrolling a Sub-rectangle of a Raster
You can scroll a sub-rectangle of a raster in any direction–up, down, left, right, or diagonally.
To perform a scroll, you use the ScrollRaster() routine and specify a dx and dy (delta-x, delta-y) by which the rectangle image should be moved relative to the (0,0) location.
As a result of this operation, the data within the rectangle will become physically smaller by the size of delta-x and delta-y, and the area vacated by the data when it has been cropped and moved is filled with the background color (color in BgPen). ScrollRaster() is affected by the Mask setting.
Here is the syntax of the ScrollRaster() function:
ScrollRaster(&rastPort, dx, dy, xmin, ymin, xmax, ymax);
The &rastPort argument is a pointer to a RastPort. The dx and dy arguments are the distances (positive, 0, or negative) to move the rectangle. The outer bounds of the sub-rectangle are defined by the xmin, xmax, ymin and ymax arguments.
Here are some examples that scroll a sub-rectangle:
/* scroll up 2 */ ScrollRaster(&rastPort, 0, 2, 10, 10, 50, 50); /* scroll right 1 */ ScrollRaster(&rastPort, -1, 0, 10, 10, 50, 50);
When scrolling a Simple Refresh window (or other layered RastPort), ScrollRaster() scrolls the appropriate existing damage region. Refer to Intuition Windows for an explanation of Simple Refresh windows and damage regions.
When scrolling a SuperBitMap window ScrollRaster() requires a properly initialized TmpRas. The TmpRas must be initialized to the size of one bitplane with a width and height the same as the SuperBitMap, using the technique described in the "Area-Fill Information" section above.
If you are using a SuperBitMap Layer, it is possible that the information in the BitMap is not fully reflected in the layer and vice-versa. Two graphics calls, CopySBitMap() and SyncSBitMap(), remedy these situations. Again, refer to Intuition Windows for more on this.
Drawing through a Stencil
The routine BltPattern() allows you to change only a very selective portion of a drawing area.
Basically, this routine lets you define a rectangular region to be affected by a drawing operation and a mask of the same size that further defines which pixels within the rectangle will be affected.
The figure below shows an example of what you can do with BltPattern(). The 0 bits are represented by blank rectangles; the 1 bits by filled-in rectangles.
In the resulting drawing, the lighter squares show where the target drawing area has been affected. Exactly what goes into the drawing area when the mask has 1’s is determined by your RastPort’s FgPen, BgPen, DrawMode and AreaPtrn fields.
You call BltPattern() with:
BltPattern(&rastport, mask, xl, yl, maxx, maxy, bytecnt)
The &rastport argument specifies the RastPort to use. The operation will be confined to a rectangular area within the RastPort specified by xl and yl (upper right corner of the rectangle) and maxx and maxy (lower right corner of the rectangle).
The mask is a pointer to the mask to use. This can be NULL, in which case a simple rectangular region is modified. Or it can be set to the address of a byte pattern which allows any arbitrary shape within the rectangle to be defined. The bytecount is the number of bytes per row for the mask (it must be an even number of bytes).
The mask parameter is a rectangularly organized, contiguously stored pattern. This means that the pattern is stored in sequential memory locations stored as (maxy - yl + 1) rows of bytecnt bytes per row. These patterns must obey the same rules as BitMaps. This means that they must consist of an even number of bytes per row and must be stored in memory beginning at a legal word address. (The mask for BltPattern() does not have to be in Chip RAM, though.)
Extracting from a Bit-packed Array
You use the routine BltTemplate() to extract a rectangular area from a source area and place it into a destination area.
The following figure shows an example.
For a rectangular bit array to be extracted from within a larger, rectangular bit array, the system must know how the larger array is organized. For this extraction to occur properly, you also need to tell the system the modulo for the inner rectangle.
The modulo is the value that must be added to the address pointer so that it points to the correct word in the next line in this rectangularly organized array.
The following figure represents a single bitplane and the smaller rectangle to be extracted.
The modulo in this instance is 4, because at the end of each line, you must add 4 to the address pointer to make it point to the first word in the smaller rectangle.
Warning |
---|
The modulo value must be an even number of bytes. |
BltTemplate() takes the following arguments:
BltTemplate(source, srcX, srcMod, &destRastPort, destX, destY, sizeX, sizeY);
The source argument specifies the rectangular bit array to use as the source template. Set this to the address of the nearest word (rounded down) that contains the first line of the source rectangle. The srcX argument gives the exact bit position (0-15) within the source address at which the rectangular bit array begins. The srcMod argument sets the source modulo so the next line of the rectangular bit array can be found.
The data from the source rectangle is copied into the destination RastPort specified by destRastPort. The destX and destY arguments indicate where the data from the source rectangle should be positioned within the destination RastPort. The sizeX and sizeY arguments indicate the dimensions of the data to be moved.
BltTemplate() uses FgPen, BgPen, DrawMode and Mask to place the template into the destination area.
This routine differs from BltPattern() in that only a solid color is deposited in the destination drawing area, with or without a second solid color as the background (as in the case of text). Also, the template can be arbitrarily bit-aligned and sized in x.
Copying Rectangular Areas
Need BltBitMapTagList() documentation added here.
Four routines use the blitter to copy rectangular areas from one section of a BitMap to another: BltBitMap(), BltMaskBitMapRastPort(), BltBitMapRastPort() and ClipBlit(). All four of these blitter routines take a special argument called a minterm.
The minterm variable is an unsigned byte value which represents an action to be performed during the move. Since all the blitter routines uses the hardware blitter to move the data, they can take advantage of the blitter’s ability to logically combine or change the data as the move is made. The most common operation is a direct copy from source area to destination, which uses a minterm set to hex value C0.
You can determine how to set the minterm variable by using the logic equations shown in the following tables. B represents data from the source rectangle and C represents data in the destination area.
Leftmost 4 Bits of MinTerm | Logic Term Included in Final Output |
---|---|
8 | BC "B AND C" |
4 | B!C "B AND NOT C" |
2 | !BC "NOT B AND C" |
1 | !B!C "NOT B AND NOT C" |
You can combine values to select the logic terms. For instance a minterm value of 0xC0 selects the first two logic terms in the table above. These logic terms specify that in the final destination area you will have data that occurs in source B only. Thus, C0 means a direct copy. The logic equation for this is:
BC + B!C = B(C + !C) = B
Logic equations may be used to decide on a number of different ways of moving the data. For your convenience, a few of the most common ones are listed below.
Minterm Value | Logic Operation Performed During Copy |
---|---|
30 | Replace destination area with inverted source B. |
50 | Replace destination area with an inverted version of itself. |
60 | Put B where C is not, put C where B is not (cookie cut). |
80 | Only put bits into destination where there is a bit in the same position for both source and destination (sieve operation). |
C0 | Plain vanilla copy from source B to destination C. |
The graphics library blitter routines all accept a minterm argument as described above. BltBitMap() is the basic blitter routine, moving data from one BitMap to another.
BltBitMap() allows you to define a rectangle within a source BitMap and copy it to a destination area of the same size in another (or even the same) BitMap. This routine is used by the graphics library itself for rendering. BltBitMap() returns the number of planes actually involved in the blit. The syntax for the function is:
uint32 planes; planes = BltBitMap(&srcBM, srcX, srcY, &dstBM, dstX, dstY, sizeX, sizeY, minterm, mask, tempA);
The source bitmap is specified by the &srcBM argument. The position of the source area within the bitmap is specified by srcX and srcY. The destination bitmap is specified by the &dstBM argument. The position of the destination area within the bitmap is specified by dstX and dstY.
The dimensions (in pixels) of the area to be moved is indicated by the sizeX and sizeY arguments. With the original custom chip set, the blitter size limits are 992 x 1,024. With ECS the blitter size limits are 32,736 x 32,768. See the section on Determining Chip Versions to find out how to tell if the host system has ECS installed.
The minterm argument determines what logical operation to perform on the rectangle data as bits are moved (described above). The mask argument, normally set to 0xff, specifies which bitplanes will be involved in the blit operation and which will be ignored. If a bit is set in the mask byte, the corresponding bitplane is included. The tempA argument applies only to blits that overlap and, if non-NULL, points to Chip memory the system will use for temporary storage during the blit.
BltBitMapRastPort() takes most of the same arguments as BltBitMap(), but its destination is a RastPort instead of a BitMap. The syntax for the function is:
VOID BltBitMapRastPort(&srcBM, srcX, srcY, &dstRP, dstX, dstY, sizeX, sizeY, minterm);
The arguments here are the same as for BltBitMap() above. Note that the BltBitMapRastPort() function will respect the RastPort.Mask field. Only the planes specified in the Mask will be included in the operation.
A third type of blitter operation is provided by the BltMaskBitMapRastPort() function. This works the same as BltBitMapRastPort() except that it takes one extra argument, a pointer to a single bitplane mask of the same height and width as the source. The mask acts as a filter for the operation–a blit only occurs where the mask plane is non-zero. The syntax for the function is:
VOID BltMaskBitMapRastPort(&srcBM, srcX, srcY, &dstRP, dstX, dstY, sizeX, sizeY, minterm, bltmask);
The bltmask argument points to a word-aligned mask bitplane in Chip memory with the same dimensions as the source bitmap. Note that this function ignores the Mask field of the destination RastPort.
ClipBlit() takes most of the same arguments as the other blitter calls described above but it works with source and destination RastPorts and their layers. Before ClipBlit() moves data, it looks at the area from which and to which the data is being copied (RastPorts, not BitMaps) and determines if there are overlapping areas involved. If so, it splits up the overall operation into a number of bitmaps to move the data in the way you request. To call ClipBlit() use:
VOID ClipBlit(&srcRP, srcX, srcY, &dstRP, dstX, dstY, XSize, YSize, minterm);
Since ClipBlit() respects the Layer of the source and destination RastPort, it is the easiest blitter movement call to use with Intuition windows. The following code fragments show how to save and restore an undo buffer using ClipBlit().
/* Save work rastport to an undo rastport */ ClipBlit(&drawRP, 0, 0, &undoRP, 0, 0, areaWidth, areaHeight, 0xC0); /* restore undo rastport to work rastport */ ClipBlit(&undoRP, 0, 0, &drawRP, 0, 0, areaWidth, areaHeight, 0xC0);
Scaling Rectangular Areas
BitMapScale() will scale a single bitmap any integral size up to 16,383 times its original size. It is called with the address of a BitScaleArgs structure (see <graphics/scale.h>).
VOID BitMapScale(struct BitScaleArgs *bsa);
The bsa argument specifies the BitMaps to use, the source and destination rectangles, as well as the scaling factor. The source and destination may not overlap. The caller must ensure that the destination BitMap is large enough to receive the scaled-up copy of the source rectangle. The function ScalerDiv() is provided to help in the calculation of the destination BitMap’s size.
When to Wait for the Blitter
This section explains why you might have to call WaitBlit(), a special graphics function that suspends your task until the blitter is idle. Many of the calls in the graphics library use the Amiga’s hardware blitter to perform their operation, most notably those which render text and images, fill or pattern, draw lines or dots and move blocks of graphic memory.
Internally, these graphics library functions operate in a loop, doing graphic operations with the blitter one plane at a time as follows:
IGraphics->OwnBlitter(); /* Gain exclusive access to the hardware blitter */ for(planes=0; planes < bitmap->depth; planes++) { IGraphics->WaitBlit(); /* Sleep until the previous blitter operation completes */ /* start a blit */ } IGraphics->DisownBlitter(); /* Release exclusive access to the hardware blitter */
Graphics library functions that are implemented this way always wait for the blitter at the start and exit right after the final blit is started. It is important to note that when these blitter-using functions return to your task, the final (or perhaps only) blit has just been started, but not necessarily completed. This is efficient if you are making many such calls in a row because the next graphics blitter call always waits for the previous blitter operation to complete before starting its first blit.
However, if you are intermixing such graphics blitter calls with other code that accesses the same graphics memory then you must first WaitBlit() to make sure that the final blit of a previous graphics call is complete before you use any of the memory. For instance, if you plan to immediately deallocate or reuse any of the memory areas which were passed to your most recent blitter-using function call as a source, destination, or mask, it is imperative that you first call WaitBlit().
Warning |
---|
If you do not follow the above procedure, you could end up with a program that works correctly most of the time but crashes sometimes. Or you may run into problems when your program is run on faster machines or under other circumstances where the blitter is not as fast as the processor. |
Function Reference
The following are brief descriptions of the Amiga’s graphics primitives. See the SDK for details on each function call.
Function | Description |
---|---|
InitView() | Initializes the View structure. |
InitBitMap() | Initializes the BitMap structure. |
RASSIZE() | Calculates the size of a ViewPort’s BitMap. |
AllocRaster() | Allocates the bitplanes needed for a BitMap. |
FreeRaster() | Frees the bitplanes created with AllocRaster(). |
InitVPort() | Initializes the ViewPort structure. |
GetColorMap() | Returns the ColorMap structure used by ViewPorts. |
FreeColorMap() | Frees the ColorMap created by GetColorMap(). |
LoadRGB4() | Loads the color registers for a given ViewPort. |
SetRGB4CM() | Loads an individual color register for a given ViewPort. |
MakeVPort() | Creates the intermediate Copper list program for a ViewPort. |
MrgCop() | Merges the intermediate Copper lists. |
LoadView() | Displays a given View. |
FreeCprList() | Frees the Copper list created with MrgCop() |
FreeVPortCopLists() | Frees the intermediate Copper lists created with MakeVPort(). |
OFF_DISPLAY | Turns the video display DMA off |
ON_DISPLAY | Turns the video display DMA back on again. |
FindDisplayInfo() | Returns the display database handle for a given ModeID. |
GetDisplayInfoData() | Looks up a display attribute in the display database. |
VideoControl() | Sets, clears and gets the attributes of an existing display. |
GfxNew() | Creates ViewExtra or ViewPortExtra used in displays. |
GfxAssociate() | Attaches a ViewExtra to a View. |
GfxFree() | Frees the ViewExtra or ViewPortExtra created by GfxNew(). |
OpenMonitor() | Returns the MonitorSpec structure used in Views. |
CloseMonitor() | Frees the MonitorSpec structure created by OpenMonitor(). |
GetVPModeID() | Returns the ModeID of an existing ViewPort. |
ModeNotAvailable() | Determines if a display mode is available from a given ModeID. |
Function | Description |
---|---|
InitRastPort() | Initialize a RastPort structure. |
InitArea() | Initialize the AreaInfo structure used with a RastPort. |
SetWrMask() | Set the RastPort.Mask. |
SetAPen() | Set the RastPort.FgPen foreground pen color. |
SetBPen() | Set the RastPort.BgPen background pen color. |
SetOPen() | Set the RastPort.AOlPen area fill outline pen color. |
SetDrMode() | Set the RastPort.DrawMode drawing mode. |
SetDrPt() | Set the RastPort.LinePtrn line drawing pattern. |
SetAfPt() | Set the RastPort area fill pattern and size. |
WritePixel() | Draw a single pixel in the foreground color at a given coordinate. |
ReadPixel() | Find the color of the pixel at a given coordinate. |
DrawCircle() | Draw a circle with a given radius and center point. |
DrawEllipse() | Draw an ellipse with the given radii and center point. |
Move() | Move the RastPort drawing pen to a given coordinate. |
Draw() | Draw a line from the current pen location to a given coordinate. |
PolyDraw() | Draw a polygon with a given set of vertices. |
AreaMove() | Set the anchor point for a filled polygon. |
AreaDraw() | Add a new vertice to an area-fill polygon. |
AreaEnd() | Close and area-fill polygon, draw it and fill it. |
BNDRYOFF() | Turn off area-outline pen usage activated with SetOPen(). |
AreaCircle() | Draw a filled circle with a given radius and center point. |
AreaEllipse() | Draw a filled ellipse with the given radii and center point. |
Flood() | Flood fill a region starting at a given coordinate. |
RectFill() | Flood fill a rectangular area at a given location and size. |
Function | Description |
---|---|
BltClear() | Use the hardware blitter to clear a block of memory. |
SetRast() | Fill the RastPort.BitMap with a given color. |
ScrollRaster() | Move a portion of a RastPort.BitMap. |
BltPattern() | Draw a rectangular pattern of pixels into a RastPort.BitMap. The x-dimension of the rectangle must be word-aligned and word-sized. |
BltTemplate() | Draw a rectangular pattern of pixels into a RastPort.BitMap. The x-dimension of the rectangle can be arbitrarily bit-aligned and sized. |
BltBitMap() | Copy a rectangular area from one BitMap to a given coordinate in another BitMap. |
BltBitMapRastPort() | Copy a rectangular area from a BitMap to a given coordinate in a RastPort.BitMap. |
BltMaskBitMapRastPort() | Copy a rectangular area from a BitMap to a RastPort.BitMap through a mask bitplane. |
ClipBlit() | Copy a rectangular area from one RastPort to another with respect to their Layers. |
BitMapScale() | Scale a rectangular area within a BitMap to new dimensions. |
Function | Description |
---|---|
OwnBlitter() | Obtain exclusive access to the Amiga’s hardware blitter. |
DisownBlitter() | Relinquish exclusive access to the blitter. |
WaitBlit() | Suspend until the current blitter operation has completed. |
QBlit() | Place a bltnode-style asynchronous blitter request in the system queue. |
QBSBlit() | Place a bltnode-style asynchronous blitter request in the beam synchronized queue. |
CINIT() | Initialize the user Copper list buffer. |
CWAIT() | Instructs the Copper to wait for the video beam to reach a given position. |
CMOVE() | Instructs the Copper to place a value into a given hardware register. |
CBump() | Instructs the Copper to increment its Copper list pointer. |
CEND() | Terminate the user Copper list. |