Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » [A5] al_draw_rectangle bug? rectangle is drawn by 1 pixel off.

Credits go to Elias and SiegeLord for helping out!
This thread is locked; no one can reply to it. rss feed Print
 1   2 
[A5] al_draw_rectangle bug? rectangle is drawn by 1 pixel off.
axilmar
Member #1,204
April 2001

I am using the function al_draw_rectangle to create a rectangle from (200, 150) to (449, 349). The rectangle's width is 250 pixels, and the height is 200 pixels. Here is the drawing code:

//test draw
void draw() {
    al_draw_filled_rectangle(0, 0, 640, 480, al_map_rgb(255, 255, 255));
    al_draw_rectangle(200, 150, 200+250-1, 150+200-1, al_map_rgb(0, 0, 0), 1);
}

However, when I zoom into the picture, the rectangle starts from (199, 149) (the cursor is over the red pixel):

{"name":"Wvqcv.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/6\/a\/6a6255b35cf08de2c944c948838070f7.png","w":1280,"h":998,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/6\/a\/6a6255b35cf08de2c944c948838070f7"}Wvqcv.png

The A5 manual says:

Quote:

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.

However, the center of pixel (199, 149) is not inside the shape. The center of this pixel is (199.5, 149.5), which is outside of the range (200.0-201.0, 150.0-151.0).

What gives? is this normal? is it an A5 bug?

If I use thickness = 0, then the rectangle still starts from the pixel (199, 149), with the exception that the pixel at that specific location is not drawn.

SiegeLord
Member #7,827
October 2006
avatar

Outlined versions need the 0.5 offsets. See the second diagram + code here: http://www.allegro.cc/manual/5/primitives.html.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

axilmar
Member #1,204
April 2001

So, when I draw an outlined shape, I need to add 0.5 to all the coordinates.

What about filled shapes? I draw a filled rectangle from (200,150) to (449, 349), but the rectangle is drawn up to (448, 348):

{"name":"eupaR.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/e\/2\/e2d2235e40e6dfff9015881b421ef495.png","w":1280,"h":998,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/e\/2\/e2d2235e40e6dfff9015881b421ef495"}eupaR.png

So I guess at for filled shapes, I need to add the offset 0.5 to the right and bottom coordinates. Am I correct?

Elias
Member #358
May 2000

No, there is no offset for filled shapes. And outlined shapes also are filled - they simply specify the center and not the outer outline. So if you use a thickness of 1 that means the filled shape extends 0.5 in all directions and the offset is only to counter that - but in general there never is any extra offset.

--
"Either help out or stop whining" - Evert

SiegeLord
Member #7,827
October 2006
avatar

I think it is best to imagine what happens exactly instead of memorising the offsets. Study the second diagram + code in the link I gave you and it should be apparent why the output you get is what you get.

The short version is that integer pixel coordinates refer to the top-left corner of the pixel and not its centre.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

axilmar
Member #1,204
April 2001

SiegeLord said:

I think it is best to imagine what happens exactly instead of memorising the offsets. Study the second diagram + code in the link I gave you and it should be apparent why the output you get is what you get.

I have studied both diagrams and read the relevant documentation page many times, but I can't seem to pinpoint the exact rules of drawing.

So, let me write down the complete set of rules of drawing:

  • if the shape is outlined, then the coordinates refer to the pixel center.

  • if the shape is not outlined, then the coordinates refer to the pixel top-left corner.

  • a pixel is drawn if its center is within the shape coordinates.

Am I correct? are the above the only rules I need to remember for drawing?

Elias
Member #358
May 2000

They always refer to the exact position (with 0.0/0.0 being the top-left corner of the top-most and left-most pixel). But when you draw an outlined shape, say the red line in the diagram from (2, 1) to (6, 1) then you need to take the thickness into account. It has a thickness of 2, so you really draw a rectangle from (2, 1 - 1) to (6, 1 + 1) so a rectangle from (2, 0) to (6, 2). Any any pixels who's pixel centers are within that mathematical shape will be lit.

[edit: thickness of al_draw_line only extends along the normal, fixed my numbers :P]

--
"Either help out or stop whining" - Evert

SiegeLord
Member #7,827
October 2006
avatar

axilmar said:

Am I correct? are the above the only rules I need to remember for drawing?

No. Understanding what pixels are drawn given the inputs you give to the functions is a two step process.

  1. Understanding how the actual polygon is formed

  2. Understanding what pixels are drawn given the actual polygon

Combining the two steps into one rule is possible, but only after you understand the two steps above.

For a filled rectangle, the actual polygon is formed exactly from the coordinates you pass to the function:

al_draw_filled_rectangle(0, 0, 100, 100, c)

will produce a rectangular polygon that spans from (0, 0) to (100, 100). Now given that the coordinate system is such that integer coordinates refer to the top left corner of the pixel, this means that pixels 0 through 99 will be shaded in a horizontal line. Pixel 100 has its center at 100.5 which is outside the polygon.

For an outlined rectangle, you have to imagine two borders being formed, the outer one and the inner one: the only pixels that will be drawn are those inside the outer border and outside the inner one. Both borders are in effect offset from the rectangle you pass by thickness/2. I.e. this call:

al_draw_rectangle(0, 0, 100, 100, c, 1)

Will have an inner border of (0.5, 0.5) to (99.5, 99.5) and an outer border of (-0.5, -0.5, 100.5, 100.5). Those 0.5's are bad because it means that the borders lie on the centers of the pixels. What happens in that case is implementation defined. To avoid that, you introduce an offset:

al_draw_rectangle(0.5, 0.5, 99.5, 99.5, c, 1)

This call will have these borders: inner: (1, 1, 99, 99), outer: (0, 0, 100, 100). Now you can see that the pixels that'll get drawn (again in a horizontal line) are pixel 0 (center at (0.5, 0.5) falls between 0 and 1) and pixel 99 (center at (99.5, 99.5) falls between 99 and 100).

So yeah... those are the "rules". Makes sure you get what would happen if your thickness was 2 instead of 1.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

axilmar
Member #1,204
April 2001

SiegeLord said:

So yeah... those are the "rules".

To which rules are you referring to? to the ones you wrote or to the ones I wrote?

SiegeLord
Member #7,827
October 2006
avatar

The ones I wrote. They are in quotes because they are not at the same level of rules that the rules you wrote were.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

axilmar
Member #1,204
April 2001

So, are these the complete set of rules?

  • if the shape is outlined, then the coordinates refer to the line center.

  • if the shape is not outlined, then the coordinates refer to the pixel top-left corner.

  • a pixel is drawn if its center is within the shape coordinates.

I changed the 'pixel center' to 'line center' in the first rule, in order to account for the line thickness, as per your comment.

SiegeLord
Member #7,827
October 2006
avatar

Well, the second rule is not quite right. What does it predict when you do this:

al_draw_filled_rectangle(0.25, 0.25, 10.25, 10.25) ?

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

axilmar
Member #1,204
April 2001

SiegeLord said:

Well, the second rule is not quite right. What does it predict when you do this:

It draws a filled rectangle from (0,0) to (9, 9).

The center of the pixel at the coordinate 0.25 is within the shape, and therefore the pixel 0 is drawn.

The center of the pixel at the coordinate 10.25 is not within the shape, and therefore the pixel 10 is not included into the shape.

How would you write the 2nd rule?

SiegeLord
Member #7,827
October 2006
avatar

Well, thing is... the coordinate system A5 uses always has integer coordinates correspond to the top-left corner of a pixel. I'd guess the second rule would be better put as coordinates refer to the shape edge, or something.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

axilmar
Member #1,204
April 2001

Mark Oates
Member #1,146
March 2001
avatar

I came in late, but the first point I'll make is that it's very helpful to turn on sub-pixel rendering. It makes it a lot clearer to see what's going on under there. Not having sub-pixel rendering can really obfuscate what's happening, especially because it's at the unit level, and can lead to much confusion.

The second point, and the real key in my opinion, is that when drawing outlines, you're drawing with a stroke aligned to the center of the coordinates.

A filled rectangle, drawn at (3, 3) to (17, 16):
{"name":"603584","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/7\/27f1091587815b3ccf5c41f143a389f5.png","w":532,"h":504,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/7\/27f1091587815b3ccf5c41f143a389f5"}603584

The outline, using the same coordinates. The stroke has a pixel width of 1 and is centered on the coordinates. The is what allegro does when drawing strokes:
{"name":"603585","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/3\/b\/3ba01788136cfe2eca1eebfe58072d10.png","w":532,"h":504,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/3\/b\/3ba01788136cfe2eca1eebfe58072d10"}603585

Here is an example of a different stroke alignment, where the stroke is inside the shape (stroke with of 1.5):
{"name":"603586","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/8\/6\/861866a517208fede80ea51858341e76.png","w":532,"h":504,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/8\/6\/861866a517208fede80ea51858341e76"}603586

This is what people typically assume will happen. In this case, it is a top and left aligned stroke:
{"name":"603587","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/1\/0\/10682a9252e67b554d7c981fce6b4b3a.png","w":532,"h":504,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/1\/0\/10682a9252e67b554d7c981fce6b4b3a"}603587

Since the alignment is centered with allegro, putting a +0.5 on your coordinates (when drawing with a stroke-width of 1px) will tuck the centered stroke inside a whole pixel when plotted to the screen, assuming you have no transformations. The result is that it will behave more like a pixel-coordinate rendering system rather than a position-coordinate.

And finally, my third point - The fact that the position-coordinates are also in pixel widths can erroneously make it seem like you are plotting directly to pixel-coordinates. Don't let that confuse you.

That's the way I see it, anyway. :P

--
Visit CLUBCATT.com for cat shirts, cat mugs, puzzles, art and more <-- coupon code ALLEGRO4LIFE at checkout and get $3 off any order of 3 or more items!

AllegroFlareAllegroFlare DocsAllegroFlare GitHub

Arthur Kalliokoski
Second in Command
February 2005
avatar

I thought the reason for all this was because particular video cards have some differences in how they render a line, and these "rules" are trying to achieve some sort of compromise that will do what you expected. ???

They all watch too much MSNBC... they get ideas.

Elias
Member #358
May 2000

I thought the reason for all this was because particular video cards have some differences in how they render a line, and these "rules" are trying to achieve some sort of compromise that will do what you expected.

No, sub-pixel rendering just looks way better (and is not possible with integer-only coordinates of course). Filled shapes are always rendered exactly no matter which video card draws them (except when the outline of your shape happens to hit the exact center of a pixel).

--
"Either help out or stop whining" - Evert

Mark Oates
Member #1,146
March 2001
avatar

--
Visit CLUBCATT.com for cat shirts, cat mugs, puzzles, art and more <-- coupon code ALLEGRO4LIFE at checkout and get $3 off any order of 3 or more items!

AllegroFlareAllegroFlare DocsAllegroFlare GitHub

Trezker
Member #1,739
December 2001
avatar

I'm bookmarking that page. I need to go over my GUI rendering code...

Neil Roy
Member #2,229
April 2002
avatar

After quite a bit of searching I haven't found anything explaining how to enable sub-pixel rendering. Given how lines are drawn now, I would think it would be on by default.

---
“I love you too.” - last words of Wanda Roy

SiegeLord
Member #7,827
October 2006
avatar

It's not supported by all cards, so enabling it by default seems tenuous. Anyway, you can look at 'ex_multisample' to see how it is done. I don't know why wiki uses the 'sub-pixel rendering' term... it'd be easier to relate to the actual feature if it referred to it as multi-sampling.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Neil Roy
Member #2,229
April 2002
avatar

Ah okay, I was thinking about multisampling, but I wasn't sure if there was some feature that I just wasn't seeing.

---
“I love you too.” - last words of Wanda Roy

Arthur Kalliokoski
Second in Command
February 2005
avatar

The term "sub pixel" refers to antialiasing (drawing screen pixels according to how close they are to where they "should" be) and multisampling is "sub texel" which refers to grabbing source texels (bitmap pixels) according to how close, right?

They all watch too much MSNBC... they get ideas.

axilmar
Member #1,204
April 2001

I think Allegro should also offer a primitives add-on that draws pixel coordinates. It's hard to remember all these details. I had to read this thread again in order to be reminded of what I should do in each case, and perhaps I am not the only one. The primitives add-on is good for games, where it pixel coordinates do not matter, but it is not so appropriate for drawing UIs, where discrete coordinates are required. It would be quite helpful to a lot of people, I think.

 1   2 


Go to: