Pixels in Direct2D

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.


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...

Tags:

Direct2D