The Allegro Wiki is migrating to github at https://github.com/liballeg/allegro_wiki/wiki

Grid Coordinates vs. Pixel Coordinates

From Allegro Wiki
Jump to: navigation, search

Because Allegro 5 draws primitives on grid coordinates, it can be confusing at first if you're expecting it to draw to pixel coordinates. This article is for you if your Allegro 5 primitives just seem to be drawing a little bit off and/or you're still having problems making the distinction between pixel space and grid space. It will help you better understand how the coordinates aren't pixels so you can not be thrown off when things don't render the way you might expect.

Differences When Drawing a Line

For illustrative purposes, let's imagine a made-up function called line() that draws a line, and in this case our function uses pixel coordinates. In this snippit of code, we'll draw a line starting at the pixel (1, 5) and ends at the pixel (16, 2). Note that when using this function, you are passing the pixel locations where you want the drawing to begin and end. Notice that the grid axes start on the pixel 0 and increments at each subsequent pixel.

// void line(int x1, int y1, int x2, int y2, int color);
line(1, 5, 16, 2, color_red);

This is what it would look like:

A4line2.png

As you can see, The render starts at the first pixel location (1, 5) filling it, and moving on to fill the rest of the pixels between it, and then finally filling (16, 2).

Drawing to pixel coordinates can cause problems, as they are not locations in a native transformable space, but rather represent a space in bitmap memory, specifically, a pixel location. Since Allegro 5 is based on hardware acceleration (plotting coordinates with transformations and the like), Allegro 5 primitives are drawn to the coordinate grid instead of drawing to pixels.

So let's see how a line is drawn in Allegro 5 using the same number coordinates that we used in our imaginary function. This time, we'll use the function al_draw_line. Unlike our imaginary line() function which draws to pixel coordinates, al_draw_line's arguments are grid coordinates. In this case, the line has a stroke thickness of 1.0 pixels:

ALLEGRO_COLOR color_blue = al_map_rgb(0, 0, 255);
al_draw_line(1.0, 5.0, 16.0, 2.0, color_blue, 1.0);

A5line2.png

Notice that (0,0) starts at the absolute left-most top-most edge of the rendering surface and one 'unit' on the grid is the width of a pixel.

Here's what it looks like when that shape is resolved into actual pixels. In this first picture, we have sub-pixel rendering on.

Al5line-sub-pixel.png

Now when we turn sub-pixel rendering off, here's where the pixels resolve.

Al5line-no-sub-pixel.png

As you can see, the result is different from what it would be if drawn using pixel coordinates.

How a Rectangle is Drawn

Knowing what we know, let's move on to a rectangle. Here's what happens when we plot a rectangle from (3, 4) to (17, 16).

ALLEGRO_COLOR color_orange = al_map_rgb(255, 210, 0);
al_draw_rectangle(3.0, 4.0, 17.0, 16.0, color_orange, 1.0);

A5-rectangle-stroke-width-of-1-pixel.png

Just as with the line before, the outline stroke is drawn centered on the coordinates that we've used. The outline plot takes up part of a pixel on either side of the line representing the shape.

Now, let's see what it looks like when it resolves to pixels with sub-pixel rendering on.

A5rect-subpixel.png

As you can see, the pixels are half-transparent renderings, as half of the actual pixel is filled with the rectangle's outline stroke. On the outside corners, a quarter of the pixel is filled, so the opacity is less, and on the inside corners there is more space taken up in the pixel, so more opacity is used.

If we would turn off sub-pixel rendering the result would depend on the used GPU as the rectangle shape goes right through the center of pixels. See further below how to draw a rectangle without sub-pixel rendering.

How a Filled Rectangle is Drawn

Since filled shapes do not have a stroke thickness, they fill completely inside the plotted coordinates.

ALLEGRO_COLOR red_color = al_map_rgb(184, 22, 22);
al_draw_filled_rectangle(3.0, 4.0, 17.0, 16.0, red_color);

Filled rectangle.png

Pixel-Precise Outlines

If you still want to plot your outlined shapes by thinking in pixel coordinates, or, you want to draw shapes where you can plot to pixels explicitly, you can do that by having your shape coincide with the pixel grid. With sub-pixel rendering either on or off, the final rendered pixels will happen to appear the same. Either way, the outline and stroke thickness of 1 will be "tucked" into the pixels and fill them.

float offset = 0.5;
ALLEGRO_COLOR color_orange = al_map_rgb(255, 210, 0);
al_draw_rectangle(3.0+offset, 4.0+offset, 17.0+offset, 16.0+offset, color_orange, 1);

A5-rectangle-offset.png

Keep in mind, if you apply transformations like scaling or rotation, the renderings could appear to be off by 0.5. You should use this hack only if you plan to keep the shape in the "pixel world." Think of it as an accommodation to rendering on a device that draws in pixels.

See Also

Pixel-Precise Output