Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » primitives glitches

Credits go to Elias and SiegeLord for helping out!
This thread is locked; no one can reply to it. rss feed Print
 1   2   3 
primitives glitches
SiegeLord
Member #7,827
October 2006
avatar

What about the coordinates? If you want to draw a 10x10 box from (0,0)-(9,9), what should you use?

EDIT: nvm. What Trent said.

Can't the custom verts use their own functions for setting? Or maybe the GFX driver provides a set of vert get/set'ers in its vtable?

No to the first one, since if they do do that, then what's the point. We change something in ALLEGRO_VERTEX, fix it via changing the accessor functions, but break the custom vertices since the user provides the accessor functions. As for the second one, yeah, I guess. I didn't say it was impossible, after all, just an incredible pain for both the developer and the user ;)

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

Thomas Fjellstrom
Member #476
June 2000
avatar

SiegeLord said:

No to the first one, since if they do do that, then what's the point. We change something in ALLEGRO_VERTEX, fix it via changing the accessor functions, but break the custom vertices since the user provides the accessor functions. As for the second one, yeah, I guess. I didn't say it was impossible, after all, just an incredible pain for both the developer and the user ;)

It sounds more like custom vertices are a horrible pain ;)

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Matthew Leverton
Supreme Loser
January 1999
avatar

Same as Allegro 4 except you add 0.5, since in Allegro 5 0.5 represents the center of a pixel. So (0.5, 0.5, 9.5, 9.5).

Does this affect everything? Say you wanted to draw a 10x10 piece of a bitmap beginning at (5,5). Would you still add 0.5?

Edit:

And is the blender supposed to affect al_draw_prim? It doesn't seem to be for me.

Thomas Fjellstrom
Member #476
June 2000
avatar

Does this affect everything? Say you wanted to draw a 10x10 piece of a bitmap beginning at (5,5). Would you still add 0.5?

Yup. a pixel coordinate is anchored to the line between pixels, rather than the center. So you have to add 0.5 to any coord to make sure it won't randomly select a pixel on either side of the line (different cards and drivers will pick a different pixel if you try and draw between pixels).

Quote:

And is the blender supposed to affect al_draw_prim? It doesn't seem to be for me.

It indeed doesn't, but it probably should. Though I think there are issues with that, and it might need a shader to make work properly. Not 100% sure though. They were just talking about this on the [AD] list. Some kind of solution was bashed out I think.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Matthew Leverton
Supreme Loser
January 1999
avatar

Ugh... So, is float precision going to be a problem?

I'm trying to draw a horizontal line with a one pixel padding inside a 50x22 rectangle. In A4 terms, from 1x1 to 48x1.

000         44  
012         89
--------------
-XXXXXXXXXXXX-
--------------

I would expect that to be: (1.5,1.5) - (48.5,1.5). But that draws:

000         44  
012         89
--------------
-XXXXXXXXXXX--   <-- two pixels left
--------------

But using (1.5,1.5) - (48.505,1.5) works.

Theoretically I should safely be able to use anywhere between 48.5 and 49.4999 as the end point, correct?

Karadoc ~~
Member #2,749
September 2002
avatar

I would have thought that anywhere between 48.1 and 48.9 would be safe... I guess that shows how little I know!

As for James Lohr's comments, fortunately A4 is still available and completely functional - so keep using it if that's what you prefer. I personally like A5, mostly for its events system, so I'll be using that from now on.

-----------

SiegeLord
Member #7,827
October 2006
avatar

EDIT: This post is bollocks! Don't read it!

I'm trying to draw a horizontal line with a one pixel padding inside a 50x22 rectangle. In A4 terms, from 1x1 to 48x1.

You're probably not using thickness = 1.

Some (hopefully) helpful discussion:

There are three things that need to be kept in mind.

  1. When rendering triangles, the bottom and right edges are not drawn.

  2. Coordinates are measured from top/left corner of a pixel

  3. Top/left edges are inclusive when counting whether a coordinate is in the pixel

Thus, here's what happens when you call al_draw_filled_rectangle(0, 0, 2, 2, c);

{"name":"601590","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/9\/2\/92fd601488ba53e563ad27c6038b989a.png","w":281,"h":282,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/9\/2\/92fd601488ba53e563ad27c6038b989a"}601590

Blue squares are pixels that are touched and drawn. Red squares are touched, but not drawn. Make sure you get why this happens given the 3 points above. Now, as Tomasu said, a tiny floating point error, and those red squares won't be touched at all. Thus, we add a 0.5 offset and get this:

{"name":"601591","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/6\/c\/6c760387146406a58d956a0fe20f5930.png","w":281,"h":282,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/6\/c\/6c760387146406a58d956a0fe20f5930"}601591

Now floating point errors won't be an issue, since we have half a pixel to work with. Now let's see what happens with outlined primitives (thickness = 1, it is insane to work with hairline lines when you want pixel perfect precision). al_draw_outlined_rectangle(0, 0, 2, 2, c, 1) gets you this:

{"name":"601592","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/8\/4\/84dd3803270118bc3239dd3fa4027d91.png","w":281,"h":282,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/8\/4\/84dd3803270118bc3239dd3fa4027d91"}601592

The dashed blue line is the 'outline' of the outlined rectangle. As you can see, the pixels drawn are completely different than those from before. In fact, some pixels are drawn at negative coordinates. Applying the 0.5 offset gives you this:

{"name":"601593","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/6\/0\/60f2de051e3bad53d2efbeecb08d55db.png","w":320,"h":282,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/6\/0\/60f2de051e3bad53d2efbeecb08d55db"}601593

Now it colors only pixels with positive values, although it still does not do a true outline of the filled rectangle. Moreover, the outline of the outline is now on the pixel border, meaning that roundoff errors will produce poor effects again.

tldr:

Filled rectangle between (inclusive) x1, y1 and x2, y2:

al_draw_filled_rectangle(x1 + 0.5, y1 + 0.5, x2 + 1.5, y2 + 1.5, c)

Outline of that rectangle (inclusive to that rectangle)

al_draw_rectangle(x1 + 1, y1 + 1, x2 + 1, y2 + 1, c, 1);

Horizontal/vertical lines alone are similar to rectangles (again, thickness = 1, rules for hairline lines are hairy), except that the coordinates you specify for them are different. For horizontal lines, for example, the x coordinates follow the rules for filled rectangles, while y coordinates follow the rules for outlined rectangles. The reverse is true for vertical lines.

And to reiterate, don't use hairline lines unless you can't avoid it. Can't avoid it means that you're using transformations, which will scale/rotate the outlines, which is often times not desirable.

I suggest wrapping up those rules into functions of your own, btw... in case we subtly alter those rules in the next release :P

Yeah... anyone want a pixel perfect drawing addon? ;)

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

Matthew Leverton
Supreme Loser
January 1999
avatar

SiegeLord said:

You're probably not using thickness = 1.

I am using 1.0 for thickness. When trying to set pixel 48 as the endpoint of a horizontal line:

  • 48.5 = fail

  • 48.502 = success

I've found that adding +0.25 on the left and +0.75 on the right gives the proper results every time (using draw_line, draw_pixel, and draw_prim). But I suppose it will totally break on any other computer or with the next version of Allegro.

Quote:

And to reiterate, don't use hairline lines unless you can't avoid it. Can't avoid it means that you're using transformations, which will scale/rotate the outlines, which is often times not desirable.

I'm trying to draw scalable widgets. Off-by-one errors are extremely obvious, making the whole thing look ridiculous.

Quote:

Yeah... anyone want a pixel perfect drawing addon?

What's the intended purpose of the current primitives add-on?

james_lohr
Member #1,947
February 2002

Since these all sound like typical OpenGL problems, you should probably also consider reading the red book.

Elias
Member #358
May 2000

With OpenGL, you should not add 0.5 to pixels. That is to draw this:

{"name":"Rect.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/f\/1\/f1ac95ba021ee1562cc8dac5c6e05f10.png","w":400,"h":400,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/f\/1\/f1ac95ba021ee1562cc8dac5c6e05f10"}Rect.png

You would use this:

al_draw_filled_rectangle(2, 2, 7, 7);

This will be a 5x5 rectangle. If you do al_draw_filled_rectangle(1.9, 1.9, 7.1, 7.1) it has the same result (as long as the midpoint filling rule is used), so this should be completely stable.

For outlines, do this:

al_draw_rectangle(2.5, 2.5, 6.5, 6.5, 1);

What happens here is, thickness of 1 means you add a radius of 0.5 for each drawn position. So the outer rectangle is 2,2,7,7 again just like before. The inner rectangle is 3,3,6,6. Again, if you add a bit to the left or right it does not matter with the midpoint fill rule.

In my experience I can use this to draw pixel perfect rectangles in a robust way. However the fact that others want to add 0.5 or 0.25 or 0.75 to things is very confusing to me... if that indeed is the case and my description is wrong, I also want a new primitives addon :/

[edit:]
Quoting from the OpenGL (ES 2.0) specification:

Quote:

The rule for determining which fragments are produced by polygon rasterization
is called point sampling. The two-dimensional projection obtained by taking
the x and y window coordinates of the polygon’s vertices is formed. Fragment
centers that lie inside of this polygon are produced by rasterization. Special treatment
is given to a fragment whose center lies on a polygon boundary edge. In
such a case we require that if two polygons lie on either side of a common edge
(with identical endpoints) on which a fragment center lies, then exactly one of the
polygons results in the production of the fragment during rasterization.

So, in essence, what I said. The problematic case is if you add 0.5 (so never do that) - because then the special rule applies. In all other cases as long as the pixel center (0.5/0.5 position) lies inside the polygon edge the pixel (fragment) is produced.

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

Matthew Leverton
Supreme Loser
January 1999
avatar

Elias said:

For outlines, do this:
al_draw_rectangle(2.5, 2.5, 6.5, 6.5, 1);

Quote:

The problematic case is if you add 0.5 (so never do that)

Aren't you contradicting yourself?

So what's the deal with drawing a horizontal line? What should be used to make sure the end point of (0,48) is drawn? If it's the same as an outline, I'd expect 48.5 with a thickness of 1... but that doesn't work on my system.

Also, if the Direct3D driver behaves differently (which in my experience months ago, it does) and there is no way to come up with numbers that work for both, then I think the primitives add-on is useless for how the majority of people would use it for 2D. (Obviously for 3D stuff, it's not important to get pixel perfection.)

Elias
Member #358
May 2000

al_draw_rectangle(2.5, 2.5, 6.5, 6.5, 1) is 100% identical [edit: of course not] has the identical outline to this:
al_draw_filled_rectangle(2, 2, 7, 7). And the inner rectangle is like this: al_draw_filled_rectangle(3, 3, 6, 6). So yes, to clarify, only for filled rectangles use whole integer positions (but compared to Allegro4, the lower right corner is 1 pixel more as it's the lower right corner of the pixel). For a line with thickness 1, you would indeed draw it from pixel center to pixel center, so yes, add the 0.5 then.

Your horizontal line would be at 48.5 then, yes.

Also, if the Direct3D driver behaves differently (which in my experience months ago, it does) and there is no way to come up with numbers that work for both, then I think the primitives add-on is useless for how the majority of people would use it for 2D. (Obviously for 3D stuff, it's not important to get pixel perfection.)

I agree. One solution is to always use OpenGL. I bought the cheapest notebook I could get with some Intel GMA gfx card and it would run my old A5 OpenGL .exes without any troubles out of the box. So while I might have been extremely lucky it seems to me OpenGL support is good enough under Windows. Of course it's only OpenGL 1.1 with no FBO support (so you can't draw into bitmaps at all) but for most games that should work out.

[edit:]
Can you post a screenshot of ex_draw of how it draws filled rectangles and rectangles with tickness 1? It draws them all at 0/0, 0.25/0.25/ 0.5/0.5 0.75/0.75 so can see the difference.

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

Trent Gamblin
Member #261
April 2000
avatar

I wouldn't call opengl 1.1 with no fbos good enough...

D3D should draw the same thing as opengl given the same inputs. At least it did last I checked.

SiegeLord
Member #7,827
October 2006
avatar

Hmm... looks like I was wrong, for some reason I thought the fragment centers were not in the place they actually are. Elias is right. Ignore my previous post.

I'm trying to draw scalable widgets. Off-by-one errors are extremely obvious, making the whole thing look ridiculous.
What's the intended purpose of the current primitives add-on?

My point was that using hairline lines when you want reproducible output is unwise, since how you render hairline lines in unspecified in OpenGL, and only has been recently defined in DirectX. Polygon rasterization is, however, robust and consistent between most all drivers and API's. Non-zero thickness should result in proper rendering between platforms. If it does not, it is bug in Allegro, since polygon rasterizing is well specified in both OpenGL and Direct3D.

As for the purpose... its simple: send polygons/lines/points in a cross-API way to the graphics card such that the same inputs give the same outputs. Due to hardware limitations, this is only possible for polygons and (maybe) points. I suppose the current high level primitives are simplistic in this sense, since they expect the user to understand all these polygon filling rules.

Now...

Also, if the Direct3D driver behaves differently (which in my experience months ago, it does) and there is no way to come up with numbers that work for both, then I think the primitives add-on is useless for how the majority of people would use it for 2D.

If it does, then it is a bug. Both API's have well defined polygon rasterization rules, and there's no reason we can't make them output the same pixels.

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

Karadoc ~~
Member #2,749
September 2002
avatar

If the allegro drawing primitives are to be changed...

In my opinion, it is very important that allegro's drawing primitives give identical results for direct3D and openGL. If d3d and openGL behave differently, Allegro should compensate to fix it - not the programmer. (In my option.)

Also, to me it seems silly that al_draw_rectangle draws something other than the outline of al_draw_filled_rectangle. Again, I'd recommend that allegro should do that appropriate adding/subtracting of 0.5 pixels to fix this up. .. but I'm sure that some people would disagree with me about this, because they want it to be consistent with openGL behavior or something. It just seems silly to me that we all need lessons on how to get these functions to draw the right rectangles - clearly they aren't behaving in an intuitive way.

-----------

SiegeLord
Member #7,827
October 2006
avatar

No, that's not the reason why a fix like that might be unwanted. My concern is how these little fixes interact with the transformations. I'll need to think more about how that works, but think of this scenario: you offset the outlined rectangle by 0.5, and then apply a scale of 20... now the rectangle is offset by 10 pixels from where you actually meant to draw it. Is that a problem? Maybe.

I am in principle fine with following this manifesto for high level primitives: "Same behaviour as in Allegro 4, but potentially counter-intuitive results when you use transformations." I mean, maybe the last point is not an issue, but I want that investigated before I commit to it. Specifically, I want the scaling of primitives to work properly: I use transformations to scale up my pixel art games to higher resolutions, I don't want that operation to look different from me just scaling the output pixels directly.

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

Karadoc ~~
Member #2,749
September 2002
avatar

You're right, it would suck if scaling transformations messed everything up. So any kind of 'auto-offset' done by allegro would need to be done after the transformation; or otherwise taking the transformation into account. I suppose this is hard to do? I wouldn't know.

-----------

SiegeLord
Member #7,827
October 2006
avatar

There is already an auto offset like that. Direct3D and OpenGL have different coordinates for the origin. In OpenGL the center of the top-left pixel is at 0.5,0.5 while in Direct3D it is at 0,0. This offset is handled by the transformations, so it should not be apparent to the user that this happens.

What Matthew may be experiencing though is that DirectX 10 might have changed the origin system again, I'm not sure... looking at this seems to suggest that the centers are at 0.5,0.5 for DirectX 10. I have no means to verify this however.

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

Michał Cichoń
Member #11,736
March 2010

At the moment scaling using transformation works perfectly for me.

"God starts from scratch too"
Windows Allegro Build Repo: http://targonski.nazwa.pl/thedmd/allegro/

Matthew Leverton
Supreme Loser
January 1999
avatar

SiegeLord said:

What Matthew may be experiencing though is that DirectX 10

I'm using OpenGL under Ubuntu.

Elias said:

Can you post a screenshot of ex_draw of how it draws filled rectangles and rectangles with tickness 1? It draws them all at 0/0, 0.25/0.25/ 0.5/0.5 0.75/0.75 so can see the difference.

{"name":"601605","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/8\/2867c082b05fbc0c292b06d38beabd84.png","w":640,"h":640,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/8\/2867c082b05fbc0c292b06d38beabd84"}601605

But I'm talking specifically about drawing horizontal lines:

{"name":"601604","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/8\/0\/80ec4542988952c0dd8bc76b00ae3e55.png","w":312,"h":356,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/8\/0\/80ec4542988952c0dd8bc76b00ae3e55"}601604

  al_draw_filled_rectangle(2, 2, 49, 49, al_map_rgb(255,255,255));
  
  al_draw_rectangle(2.5, 2.5, 48.5, 48.5, al_map_rgb(128,128,128), 1);
  
  al_draw_line(2.5, 1.5, 48.5, 1.5, al_map_rgb(255,255,255), 1);

The line is drawn shorter than the outline rectangle, which isn't what I would expect.

Somebody who knows how this works just needs to update the documentation that tells you how to draw a line/box/triangle/etc from a to b. Most people aren't going to care about why the functions are not intuitive; they are just going to want to know how to use them...

Elias
Member #358
May 2000

I'd say there's a bug in al_draw_line.

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

SiegeLord
Member #7,827
October 2006
avatar

It's no more of a bug than the coordinates for al_draw_rectangle and al_draw_filled_rectangle not lining up. It should be:

al_draw_line(2, 1.5, 49, 1.5, al_map_rgb(255,255,255), 1);

Because that call is equivalent to:

al_draw_filled_rectangle(2, 1, 49, 2, al_map_rgb(255,255,255));

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

Elias
Member #358
May 2000

Ah, yes, of course.

If you want horizontal/vertical lines, best to just use al_draw_filled_rectangle anyway to avoid this confusion.

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

Trent Gamblin
Member #261
April 2000
avatar

IMO it's pretty bad when your line drawing function can't get perfectly horizontal or vertical lines right :P.

Elias
Member #358
May 2000

Well it can, you just have to be aware that with a thickness of 1, the center is in the middle of a pixel.

But I guess Siegelord is about to change that in a way so if you never use fractional coordinates you always get full pixels...

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

 1   2   3 


Go to: