Pixels in Direct2D
Asked Answered
S

2

12

enter image description here

The dark gray lines are supposed to be black and 1 pixel wide:

pRT->DrawLine(Point2F(100, 120), Point2F(300, 120), blackbrush, 1);

The light gray lines are supposed to be black and 0.5 pixel wide:

pRT->DrawLine(Point2F(120, 130), Point2F(280, 130), blackbrush, 0.5);

Instead, they are both 2 pixels wide. If I ask for 2 pixels wide, the line is black, but naturally 2 pixels wide.

The render target has the same size as the client area of the window. I would like pixel accuracy like in GDI, one coordinate = one pixel and pure colors...

Thanks.

Swaraj answered 26/5, 2012 at 5:10 Comment(0)
Q
21

Direct2D is rendering correctly. When you give it a pixel coordinate such as (100, 120), that refers to the top and left corner of the pixel element that spans from pixel coordinates (100, 120) to (101, 121) (top/left are inclusive, right/bottom are exclusive). Since it's a straight horizontal line you are effectively getting a filled rectangle from (99.5, 119.5) - (300.5, 120.5). Since the edges of this spill into adjacent pixels, that's why you're getting "2 pixel width" lines at the "wrong" brightness. You must think in terms of pixel coordinates (points with no area) and pixel elements (physical points on the screen with an area of 1x1, or just 1 of course).

If you want to draw a straight line from that covers the pixels (100, 120) to (300, 120), you should either use SemMike's suggestion of using aliased rendering (which is great for straight lines!), or you can use half-pixel offsets (because strokeWidth=1; for other strokeWidths, adjust by strokeWidth/2). Drawing from (100.5, 120.5) - (299.5, 120.5) with a stroke width of 1.0 will get you what you're looking for. That stroke extends around the pixel coordinates you specify, so you will get the "filled rectangle" over the pixel elements (100, 120) - (300, 121). And again, that's an exclusive range, so 'y=121' isn't actually filled, neither is x=300.

If you're wondering why this doesn't happen with something like GDI, it's because it doesn't do antialiased rendering, so everything always snaps to pixel elements. If you're wondering why this doesn't happen with WPF while using Shapes, it's because it uses layout rounding (UseLayoutRounding) and pixel snapping. Direct2D does not provide those services because it's a relatively low-level API.

Quantum answered 27/5, 2012 at 11:43 Comment(2)
Thanks for the explanation, makes sense. Did I miss this in the MSDN docs on Direct2D somewhere or is it not there and I was supposed to know? "Drawing from (100.5, 120.5) - (299.5, 120.5) with a stroke width of 1.0 will get you what you're looking for" If I understand you correctly, that would be (100.5, 120.5) - (300.5, 120.5). Otherwise the last pixel of the line is omitted. By the way, "SemMike's suggestion": same guy, I was answering my own question...Swaraj
Whether you use 299.5 or 300.5 depends on whether you want a 200px wide line or a 201px line. And this is probably not in the docs because it's fairly standard stuff for modern 2D graphics APIs, but it's also a bit non-obvious and advanced, and often people who're working on advanced stuff forget what they didn't know before they were that advanced. I find it helps to think of it like a sheet of grid paper. Where the horizontal and vertical lines intersect are the integer pixel coordinates, and the squares between them are the "physical" pixel elements.Quantum
S
5

You can play around with pRenderTarget->DrawLine(Point2F(100-0.5, 120-0.5), Point2F(300-0.5, 120-0.5), blackbrush, 1), but it becomes rapidly tricky. The simplest is:

pRenderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);

Hope it helps somebody...

Swaraj answered 26/5, 2012 at 7:41 Comment(2)
I wouldn't recommend avoiding the correct mental picture. The minute you need to antialias or draw a shape that is not an axis aligned line, your code (written on an incorrect mental assumption) will confuse you. This is standard stuff, the same for GDI+ as well.Prepossession
Except in DX11, texel co-ordinates are midpoint oriented, so it's not as 'standard' as it once was.Bunting

© 2022 - 2024 — McMap. All rights reserved.