Primitives addon
- General
- High level drawing routines
- Pixel-precise output
- al_draw_line
- al_draw_triangle
- al_draw_filled_triangle
- al_draw_rectangle
- al_draw_filled_rectangle
- al_draw_rounded_rectangle
- al_draw_filled_rounded_rectangle
- al_calculate_arc
- al_draw_ellipse
- al_draw_filled_ellipse
- al_draw_circle
- al_draw_filled_circle
- al_draw_arc
- al_calculate_spline
- al_draw_spline
- al_calculate_ribbon
- al_draw_ribbon
- Low level drawing routines
- Structures and types
These functions are declared in the following header file. Link with allegro_primitives.
#include <allegro5/allegro_primitives.h>
General
al_get_allegro_primitives_version
uint32_t al_get_allegro_primitives_version(void)
Returns the (compiled) version of the addon, in the same format as al_get_allegro_version.
al_init_primitives_addon
bool al_init_primitives_addon(void)
Initializes the primitives addon.
Returns: True on success, false on failure.
al_shutdown_primitives_addon
void al_shutdown_primitives_addon(void)
Shut down the primitives addon. This is done automatically at program exit, but can be called any time the user wishes as well.
See also: al_init_primitives_addon
High level drawing routines
High level drawing routines encompass the most common usage of this addon: to draw geometric primitives, both smooth (variations on the circle theme) and piecewise linear. Outlined primitives support the concept of thickness with two distinct modes of output: hairline lines and thick lines. Hairline lines are specifically designed to be exactly a pixel wide, and are commonly used for drawing outlined figures that need to be a pixel wide. Hairline thickness is designated as thickness less than or equal to 0. Unfortunately, the exact rasterization rules for drawing these hairline lines vary from one video card to another, and sometimes leave gaps where the lines meet. If that matters to you, then you should use thick lines. In many cases, having a thickness of 1 will produce 1 pixel wide lines that look better than hairline lines. Obviously, hairline lines cannot replicate thicknesses greater than 1. Thick lines grow symmetrically around the generating shape as thickness is increased.
Pixel-precise output
While normally you should not be too concerned with which pixels are displayed when the high level primitives are drawn, it is nevertheless possible to control that precisely by carefully picking the coordinates at which you draw those primitives.
To be able to do that, however, it is critical to understand how GPU cards convert shapes to pixels. Pixels are not the smallest unit that can be addressed by the GPU. Because the GPU deals with floating point coordinates, it can in fact assign different coordinates to different parts of a single pixel. To a GPU, thus, a screen is composed of a grid of squares that have width and length of 1. The top left corner of the top left pixel is located at (0, 0). Therefore, the center of that pixel is at (0.5, 0.5). The basic rule that determines which pixels are associated with which shape is then as follows: a pixel is treated to belong to a shape if the pixel's center is located in that shape. The figure below illustrates the above concepts:
This figure depicts three shapes drawn at the top left of the screen: an orange and green rectangles and a purple circle. On the left are the mathematical descriptions of pixels on the screen and the shapes to be drawn. On the right is the screen output. Only a single pixel has its center inside the circle, and therefore only a single pixel is drawn on the screen. Similarly, two pixels are drawn for the orange rectangle. Since there are no pixels that have their centers inside the green rectangle, the output image has no green pixels.
Here is a more practical example. The image below shows the output of this code:
/* blue vertical line */
al_draw_line(0.5, 0, 0.5, 6, color_blue, 1);
/* red horizontal line */
al_draw_line(2, 1, 6, 1, color_red, 2);
/* green filled rectangle */
al_draw_filled_rectangle(3, 4, 5, 5, color_green);
/* purple outlined rectangle */
al_draw_rectangle(2.5, 3.5, 5.5, 5.5, color_purple, 1);
It can be seen that lines are generated by making a rectangle based on the dashed line between the two endpoints. The thickness causes the rectangle to grow symmetrically about that generating line, as can be seen by comparing the red and blue lines. Note that to get proper pixel coverage, the coordinates passed to the al_draw_line
had to be offset by 0.5 in the appropriate dimensions.
Filled rectangles are generated by making a rectangle between the endpoints passed to the al_draw_filled_rectangle
.
Outlined rectangles are generated by symmetrically expanding an outline of a rectangle. With thickness of 1, as depicted in the diagram, this means that an offset of 0.5 is needed for both sets of endpoint coordinates.
The above rules only apply when multisampling is turned off. When multisampling is turned on, the area of a pixel that is covered by a shape is taken into account when choosing what color to draw there. This also means that shapes no longer have to contain the pixel's center to affect its color. For example, the green rectangle in the first diagram may in fact be drawn as two (or one) semi-transparent pixels. The advantages of multisampling is that slanted shapes will look smoother because they will not have jagged edges. A disadvantage of multisampling is that it may make vertical and horizontal edges blurry. While the exact rules for multisampling are unspecified, and may vary from GPU to GPU it is usually safe to assume that as long as a pixel is either completely covered by a shape or completely not covered, then the shape edges will be sharp. The offsets used in the second diagram were chosen so that this is the case: if you use those offsets, your shapes (if they are oriented the same way as they are on the diagram) should look the same whether multisampling is turned on or off.
al_draw_line
void al_draw_line(float x1, float y1, float x2, float y2,
ALLEGRO_COLOR color, float thickness)
Draws a line segment between two points.
Parameters:
- x1, y1, x2, y2 - Start and end points of the line
- color - Color of the line
- thickness - Thickness of the line, pass
<= 0
to draw hairline lines
al_draw_triangle
void al_draw_triangle(float x1, float y1, float x2, float y2,
float x3, float y3, ALLEGRO_COLOR color, float thickness)
Draws an outlined triangle.
Parameters:
- x1, y1, x2, y2, x3, y3 - Three points of the triangle
- color - Color of the triangle
- thickness - Thickness of the lines, pass
<= 0
to draw hairline lines
al_draw_filled_triangle
void al_draw_filled_triangle(float x1, float y1, float x2, float y2,
float x3, float y3, ALLEGRO_COLOR color)
Draws a filled triangle.
Parameters:
- x1, y1, x2, y2, x3, y3 - Three points of the triangle
- color - Color of the triangle
al_draw_rectangle
void al_draw_rectangle(float x1, float y1, float x2, float y2,
ALLEGRO_COLOR color, float thickness)
Draws an outlined rectangle.
Parameters:
- x1, y1, x2, y2 - Upper left and lower right points of the rectangle
- color - Color of the rectangle
- thickness - Thickness of the lines, pass
<= 0
to draw hairline lines
al_draw_filled_rectangle
void al_draw_filled_rectangle(float x1, float y1, float x2, float y2,
ALLEGRO_COLOR color)
Draws a filled rectangle.
Parameters:
- x1, y1, x2, y2 - Upper left and lower right points of the rectangle
- color - Color of the rectangle
al_draw_rounded_rectangle
void al_draw_rounded_rectangle(float x1, float y1, float x2, float y2,
float rx, float ry, ALLEGRO_COLOR color, float thickness)
Draws an outlined rounded rectangle.
Parameters:
- x1, y1, x2, y2 - Upper left and lower right points of the rectangle
- color - Color of the rectangle
- rx, ry - The radii of the round
- thickness - Thickness of the lines, pass
<= 0
to draw hairline lines
al_draw_filled_rounded_rectangle
void al_draw_filled_rounded_rectangle(float x1, float y1, float x2, float y2,
float rx, float ry, ALLEGRO_COLOR color)
Draws an filled rounded rectangle.
Parameters:
- x1, y1, x2, y2 - Upper left and lower right points of the rectangle
- color - Color of the rectangle
- rx, ry - The radii of the round
al_calculate_arc
void al_calculate_arc(float* dest, int stride, float cx, float cy,
float rx, float ry, float start_theta, float delta_theta, float thickness,
int num_segments)
Calculates an elliptical arc, and sets the vertices in the destination buffer to the calculated positions. If thickness <= 0
, then num_points
of points are required in the destination, otherwise twice as many are needed. The destination buffer should consist of regularly spaced (by distance of stride
bytes) doublets of floats, corresponding to x and y coordinates of the vertices.
Parameters:
- dest - The destination buffer
- stride - Distance (in bytes) between starts of successive pairs of coordinates
- cx, cy - Center of the arc
- rx, ry - Radii of the arc
- start_theta - The initial angle from which the arc is calculated
- delta_theta - Angular span of the arc (pass a negative number to switch direction)
- thickness - Thickness of the arc
- num_points - The number of points to calculate
al_draw_ellipse
void al_draw_ellipse(float cx, float cy, float rx, float ry,
ALLEGRO_COLOR color, float thickness)
Draws an outlined ellipse.
Parameters:
- cx, cy - Center of the ellipse
- rx, ry - Radii of the ellipse
- color - Color of the ellipse
- thickness - Thickness of the ellipse, pass
<= 0
to draw a hairline ellipse
al_draw_filled_ellipse
void al_draw_filled_ellipse(float cx, float cy, float rx, float ry,
ALLEGRO_COLOR color)
Draws a filled ellipse.
Parameters:
- cx, cy - Center of the ellipse
- rx, ry - Radii of the ellipse
- color - Color of the ellipse
al_draw_circle
void al_draw_circle(float cx, float cy, float r, ALLEGRO_COLOR color,
float thickness)
Draws an outlined circle.
Parameters:
- cx, cy - Center of the circle
- r - Radius of the circle
- color - Color of the circle
- thickness - Thickness of the circle, pass
<= 0
to draw a hairline circle
al_draw_filled_circle
void al_draw_filled_circle(float cx, float cy, float r, ALLEGRO_COLOR color)
Draws a filled circle.
Parameters:
- cx, cy - Center of the circle
- r - Radius of the circle
- color - Color of the circle
al_draw_arc
void al_draw_arc(float cx, float cy, float r, float start_theta,
float delta_theta, ALLEGRO_COLOR color, float thickness)
Draws an arc.
Parameters:
- cx, cy - Center of the arc
- r - Radius of the arc
- color - Color of the arc
- start_theta - The initial angle from which the arc is calculated
- delta_theta - Angular span of the arc (pass a negative number to switch direction)
- thickness - Thickness of the circle, pass
<= 0
to draw hairline circle
al_calculate_spline
void al_calculate_spline(float* dest, int stride, float points[8],
float thickness, int num_segments)
Calculates a Bézier spline given 4 control points. If thickness <= 0
, then num_segments
of points are required in the destination, otherwise twice as many are needed. The destination buffer should consist of regularly spaced (by distance of stride bytes) doublets of floats, corresponding to x and y coordinates of the vertices.
Parameters:
- dest - The destination buffer
- stride - Distance (in bytes) between starts of successive pairs of coordinates
- points - An array of 4 pairs of coordinates of the 4 control points
- thickness - Thickness of the spline ribbon
- num_segments - The number of points to calculate
al_draw_spline
void al_draw_spline(float points[8], ALLEGRO_COLOR color, float thickness)
Draws a Bézier spline given 4 control points.
Parameters:
- points - An array of 4 pairs of coordinates of the 4 control points
- color - Color of the spline
- thickness - Thickness of the spline, pass
<= 0
to draw a hairline spline
al_calculate_ribbon
void al_calculate_ribbon(float* dest, int dest_stride, const float *points,
int points_stride, float thickness, int num_segments)
Calculates a ribbon given an array of points. The ribbon will go through all of the passed points. If thickness <= 0
, then num_segments
of points are required in the destination buffer, otherwise twice as many are needed. The destination and the points buffer should consist of regularly spaced doublets of floats, corresponding to x and y coordinates of the vertices.
Parameters:
- dest - Pointer to the destination buffer
- dest_stride - Distance (in bytes) between starts of successive pairs of coordinates in the destination buffer
- points - An array of pairs of coordinates for each point
- points_stride - Distance (in bytes) between starts successive pairs of coordinates in the points buffer
- thickness - Thickness of the spline ribbon
- num_segments - The number of points to calculate
al_draw_ribbon
void al_draw_ribbon(const float *points, int points_stride, ALLEGRO_COLOR color,
float thickness, int num_segments)
Draws a ribbon given given an array of points. The ribbon will go through all of the passed points.
Parameters:
- points - An array of pairs of coordinates for each point
- color - Color of the spline
- thickness - Thickness of the spline, pass
<= 0
to draw hairline spline
Low level drawing routines
Low level drawing routines allow for more advanced usage of the addon, allowing you to pass arbitrary sequences of vertices to draw to the screen. These routines also support using textures on the primitives with some restrictions. For maximum portability, you should only use textures that have dimensions that are a power of two, as not every videocard supports them completely. This warning is relaxed, however, if the texture coordinates never exit the boundaries of a single bitmap (i.e. you are not having the texture repeat/tile). As long as that is the case, any texture can be used safely. Sub-bitmaps work as textures, but cannot be tiled.
A note about pixel coordinates. In OpenGL the texture coordinate (0, 0) refers to the top left corner of the pixel. This confuses some drivers, because due to rounding errors the actual pixel sampled might be the pixel to the top and/or left of the (0, 0) pixel. To make this error less likely it is advisable to offset the texture coordinates you pass to the al_draw_prim by (0.5, 0.5) if you need precise pixel control. E.g. to refer to pixel (5, 10) you'd set the u and v to 5.5 and 10.5 respectively.
al_draw_prim
int al_draw_prim(const void* vtxs, const ALLEGRO_VERTEX_DECL* decl,
ALLEGRO_BITMAP* texture, int start, int end, int type)
Draws a subset of the passed vertex buffer.
Parameters:
- texture - Texture to use, pass 0 to use only color shaded primitves
- vtxs - Pointer to an array of vertices
- decl - Pointer to a vertex declaration. If set to NULL, the vertices are assumed to be of the ALLEGRO_VERTEX type
- start - Start index of the subset of the vertex buffer to draw
- end - One past the last index of subset of the vertex buffer to draw
- type - Primitive type to draw
Returns: Number of primitives drawn
For example to draw a textured triangle you could use:
ALLEGRO_VERTEX v[] = {
{.x = 128, .y = 0, .z = 0, .u = 128, .v = 0},
{.x = 0, .y = 256, .z = 0, .u = 0, .v = 256},
{.x = 256, .y = 256, .z = 0, .u = 256, .v = 256}};
al_draw_prim(v, NULL, texture, 0, 3, ALLEGRO_PRIM_TRIANGLE_LIST);
See Also: ALLEGRO_VERTEX, ALLEGRO_PRIM_TYPE, ALLEGRO_VERTEX_DECL, al_draw_indexed_prim
al_draw_indexed_prim
int al_draw_indexed_prim(const void* vtxs, const ALLEGRO_VERTEX_DECL* decl,
ALLEGRO_BITMAP* texture, const int* indices, int num_vtx, int type)
Draws a subset of the passed vertex buffer. This function uses an index array to specify which vertices to use.
Parameters:
- texture - Texture to use, pass 0 to use only shaded primitves
- vtxs - Pointer to an array of vertices
- decl - Pointer to a vertex declaration. If set to 0, the vtxs are assumed to be of the ALLEGRO_VERTEX type
- indices - An array of indices into the vertex buffer
- num_vtx - Number of indices from the indices array you want to draw
- type - Primitive type to draw
Returns: Number of primitives drawn
See Also: ALLEGRO_VERTEX, ALLEGRO_PRIM_TYPE, ALLEGRO_VERTEX_DECL, al_draw_prim
al_create_vertex_decl
ALLEGRO_VERTEX_DECL* al_create_vertex_decl(const ALLEGRO_VERTEX_ELEMENT* elements, int stride)
Creates a vertex declaration, which describes a custom vertex format.
Parameters:
- elements - An array of ALLEGRO_VERTEX_ELEMENT structures.
- stride - Size of the custom vertex structure
Returns: Newly created vertex declaration.
See Also: ALLEGRO_VERTEX_ELEMENT, ALLEGRO_VERTEX_DECL, al_destroy_vertex_decl
al_destroy_vertex_decl
void al_destroy_vertex_decl(ALLEGRO_VERTEX_DECL* decl)
Destroys a vertex declaration.
Parameters:
- decl - Vertex declaration to destroy
See Also: ALLEGRO_VERTEX_ELEMENT, ALLEGRO_VERTEX_DECL, al_create_vertex_decl
al_draw_soft_triangle
void al_draw_soft_triangle(
ALLEGRO_VERTEX* v1, ALLEGRO_VERTEX* v2, ALLEGRO_VERTEX* v3, uintptr_t state,
void (*init)(uintptr_t, ALLEGRO_VERTEX*, ALLEGRO_VERTEX*, ALLEGRO_VERTEX*),
void (*first)(uintptr_t, int, int, int, int),
void (*step)(uintptr_t, int),
void (*draw)(uintptr_t, int, int, int))
Draws a triangle using the software rasterizer and user supplied pixel functions. For help in understanding what these functions do, see the implementation of the various shading routines in addons/primitives/tri_soft.c. The triangle is drawn in two segments, from top to bottom. The segments are deliniated by the vertically middle vertex of the triangle. One of each segment may be absent if two vertices are horizontally collinear.
Parameters:
- v1, v2, v3 - The three vertices of the triangle
- state - A pointer to a user supplied struct, this struct will be passed to all the pixel functions
- init - Called once per call before any drawing is done. The three points passed to it may be altered by clipping.
- first - Called twice per call, once per triangle segment. It is passed 4 parameters, the first two are the coordinates of the initial pixel drawn in the segment. The second two are the left minor and the left major steps, respectively. They represent the sizes of two steps taken by the rasterizer as it walks on the left side of the triangle. From then on, the each step will either be classified as a minor or a major step, corresponding to the above values.
- step - Called once per scanline. The last parameter is set to 1 if the step is a minor step, and 0 if it is a major step.
- draw - Called once per scanline. The function is expected to draw the scanline starting with a point specified by the first two parameters (corresponding to x and y values) going to the right until it reaches the value of the third parameter (the x value of the end point). All coordinates are inclusive.
al_draw_soft_line
void al_draw_soft_line(ALLEGRO_VERTEX* v1, ALLEGRO_VERTEX* v2, uintptr_t state,
void (*first)(uintptr_t, int, int, ALLEGRO_VERTEX*, ALLEGRO_VERTEX*),
void (*step)(uintptr_t, int),
void (*draw)(uintptr_t, int, int))
Draws a line using the software rasterizer and user supplied pixel functions. For help in understanding what these functions do, see the implementation of the various shading routines in addons/primitives/line_soft.c. The line is drawn top to bottom.
Parameters:
- v1, v2 - The two vertices of the line
- state - A pointer to a user supplied struct, this struct will be passed to all the pixel functions
- first - Called before drawing the first pixel of the line. It is passed the coordinates of this pixel, as well as the two vertices above. The passed vertices may have been altered by clipping.
- step - Called once per pixel. The second parameter is set to 1 if the step is a minor step, and 0 if this step is a major step. Minor steps are taken only either in x or y directions. Major steps are taken in both directions diagonally. In all cases, the the absolute value of the change in coordinate is at most 1 in either direction.
- draw - Called once per pixel. The function is expected to draw the pixel at the coordinates passed to it.
Structures and types
ALLEGRO_VERTEX
typedef struct ALLEGRO_VERTEX ALLEGRO_VERTEX;
Defines the generic vertex type, with a 3D position, color and texture coordinates for a single texture. Note that at this time, the software driver for this addon cannot render 3D primitives. If you want a 2D only primitive, set z to 0. Note that when you must initialize all members of this struct when you're using it. One exception to this rule are the u and v variables which can be left uninitialized when you are not using textures.
Fields:
- x, y, z - Position of the vertex
- color - ALLEGRO_COLOR structure, storing the color of the vertex
- u, v - Texture coordinates measured in pixels
See Also: ALLEGRO_PRIM_ATTR
ALLEGRO_VERTEX_DECL
typedef struct ALLEGRO_VERTEX_DECL ALLEGRO_VERTEX_DECL;
A vertex declaration. This opaque structure is responsible for describing the format and layout of a user defined custom vertex. It is created and destroyed by specialized functions.
See Also: al_create_vertex_decl, al_destroy_vertex_decl, ALLEGRO_VERTEX_ELEMENT
ALLEGRO_VERTEX_ELEMENT
typedef struct ALLEGRO_VERTEX_ELEMENT ALLEGRO_VERTEX_ELEMENT;
A small structure describing a certain element of a vertex. E.g. the position of the vertex, or its color. These structures are used by the al_create_vertex_decl function to create the vertex declaration. For that they generally occur in an array. The last element of such an array should have the attribute field equal to 0, to signify that it is the end of the array. Here is an example code that would create a declaration describing the ALLEGRO_VERTEX structure (passing this as vertex declaration to al_draw_prim would be identical to passing NULL):
/* On compilers without the offsetof keyword you need to obtain the
* offset with sizeof and make sure to account for packing.
*/
ALLEGRO_VERTEX_ELEMENT elems[] = {
{ALLEGRO_PRIM_POSITION, ALLEGRO_PRIM_FLOAT_3, offsetof(ALLEGRO_VERTEX, x)},
{ALLEGRO_PRIM_TEX_COORD_PIXEL, ALLEGRO_PRIM_FLOAT_2, offsetof(ALLEGRO_VERTEX, u)},
{ALLEGRO_PRIM_COLOR_ATTR, 0, offsetof(ALLEGRO_VERTEX, color)},
{0, 0, 0}
};
ALLEGRO_VERTEX_DECL* decl = al_create_vertex_decl(elems, sizeof(ALLEGRO_VERTEX));
Fields:
- attribute - A member of the ALLEGRO_PRIM_ATTR enumeration, specifying what this attribute signifies
- storage - A member of the ALLEGRO_PRIM_STORAGE enumeration, specifying how this attribute is stored
- offset - Offset in bytes from the beginning of the custom vertex structure. C function offsetof is very useful here.
See Also: al_create_vertex_decl, ALLEGRO_VERTEX_DECL, ALLEGRO_PRIM_STORAGE
ALLEGRO_PRIM_TYPE
typedef enum ALLEGRO_PRIM_TYPE
Enumerates the types of primitives this addon can draw.
ALLEGRO_PRIM_POINT_LIST - A list of points, each vertex defines a point
ALLEGRO_PRIM_LINE_LIST - A list of lines, sequential pairs of vertices define disjointed lines
ALLEGRO_PRIM_LINE_STRIP - A strip of lines, sequential vertices define a strip of lines
ALLEGRO_PRIM_LINE_LOOP - Like a line strip, except at the end the first and the last vertices are also connected by a line
ALLEGRO_PRIM_TRIANGLE_LIST - A list of triangles, sequential triplets of vertices define disjointed triangles
ALLEGRO_PRIM_TRIANGLE_STRIP - A strip of triangles, sequential vertices define a strip of triangles
ALLEGRO_PRIM_TRIANGLE_FAN - A fan of triangles, all triangles share the first vertex
ALLEGRO_PRIM_ATTR
typedef enum ALLEGRO_PRIM_ATTR
Enumerates the types of vertex attributes that a custom vertex may have.
ALLEGRO_PRIM_POSITION - Position information, can be stored in any supported fashion
ALLEGRO_PRIM_COLOR_ATTR - Color information, stored in an ALLEGRO_COLOR. The storage field of ALLEGRO_VERTEX_ELEMENT is ignored
ALLEGRO_PRIM_TEX_COORD - Texture coordinate information, can be stored only in ALLEGRO_PRIM_FLOAT_2 and ALLEGRO_PRIM_SHORT_2. These coordinates are normalized by the width and height of the texture, meaning that the bottom-right corner has texture coordinates of (1, 1).
ALLEGRO_PRIM_TEX_COORD_PIXEL - Texture coordinate information, can be stored only in ALLEGRO_PRIM_FLOAT_2 and ALLEGRO_PRIM_SHORT_2. These coordinates are measured in pixels.
See Also: ALLEGRO_VERTEX_DECL, ALLEGRO_PRIM_STORAGE
ALLEGRO_PRIM_STORAGE
typedef enum ALLEGRO_PRIM_STORAGE
Enumerates the types of storage an attribute of a custom vertex may be stored in.
- ALLEGRO_PRIM_FLOAT_2 - A doublet of floats
- ALLEGRO_PRIM_FLOAT_3 - A triplet of floats
- ALLEGRO_PRIM_SHORT_2 - A doublet of shorts
See Also: ALLEGRO_PRIM_ATTR
ALLEGRO_VERTEX_CACHE_SIZE
#define ALLEGRO_VERTEX_CACHE_SIZE 256
Defines the size of the transformation vertex cache for the software renderer. If you pass less than this many vertices to the primitive rendering functions you will get a speed boost. This also defines the size of the cache vertex buffer, used for the high-level primitives. This corresponds to the maximum number of line segments that will be used to form them.
ALLEGRO_PRIM_QUALITY
#define ALLEGRO_PRIM_QUALITY 10
Defines the quality of the quadratic primitives. At 10, this roughly corresponds to error of less than half of a pixel.