How can I render two adjacent triangles with XRenderCompositeTriangles?

0

I'm trying to use the triangle primitive in XRender to draw stroked lines. According to the (quite limited) documentation (see section 10):

Abutting edges must match precisely. When specifying two polygons abutting along a common edge, if that edge is specified with the same coordinates in each polygon then the sum of alpha values for pixels inside the union of the two polygons must be precisely one.

To my reading this means that if I render a white square tessellated into two triangles, all the pixels--in particular the ones along the border--should all be white. However, what I find is that the triangles effectively do not quite touch. The following code results in the output:

000 000 000 000 000 000 000 000 000 000 000 000 000
000 000 000 000 000 000 000 000 000 000 000 000 000
000 000 fff fff fff fff fff fff fff fff ccc 000 000
000 000 fff fff fff fff fff fff fff ddd fff 000 000
000 000 fff fff fff fff fff fff eee eee fff 000 000
000 000 fff fff fff fff fff fff ddd fff fff 000 000
000 000 fff fff fff fff fff ccc fff fff fff 000 000
000 000 fff fff fff fff bbb fff fff fff fff 000 000
000 000 fff fff fff ccc fff fff fff fff fff 000 000
000 000 fff fff ddd fff fff fff fff fff fff 000 000
000 000 fff eee eee fff fff fff fff fff fff 000 000
000 000 fff ccc fff fff fff fff fff fff fff 000 000
000 000 ccc fff fff fff fff fff fff fff fff 000 000
000 000 000 000 000 000 000 000 000 000 000 000 000
000 000 000 000 000 000 000 000 000 000 000 000 000

I am expecting that all the points in the rectangle would be fff, since the coordinates along the edge are the same, so all the pixels within the union of the two triangles (the rectangle) should be "precisely one". It seems like this is an essential quality, otherwise triangles aren't useful for much; if triangles leave borders then you cannot tessellate anything. So I hope I am doing something wrong, but I can't figure out what.

#include <X11/Xlib.h>
#include <X11/extensions/Xrender.h>

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

XRenderColor makeColor(float r, float g, float b, float a = 1.0f)
{
    unsigned short ur = (unsigned short)std::min(65535.0f, r * 65536.0f);
    unsigned short ug = (unsigned short)std::min(65535.0f, g * 65536.0f);
    unsigned short ub = (unsigned short)std::min(65535.0f, b * 65536.0f);
    unsigned short ua = (unsigned short)std::min(65535.0f, a * 65536.0f);
    return { ur, ug, ub, ua };
}

XTriangle makeTri(float x1, float y1, float x2, float y2, float x3, float y3)
{
    XTriangle tri;
    tri.p1.x = XDoubleToFixed(x1);
    tri.p1.y = XDoubleToFixed(y1);
    tri.p2.x = XDoubleToFixed(x2);
    tri.p2.y = XDoubleToFixed(y2);
    tri.p3.x = XDoubleToFixed(x3);
    tri.p3.y = XDoubleToFixed(y3);
    return tri;
}

int main(int argc, char *argv[])
{
    const int width = 13;
    const int height = 15;

    auto *display = XOpenDisplay(nullptr);

    // Create destination pixmap
    auto pictFormat = XRenderFindStandardFormat(display, PictStandardARGB32);
    auto drawable = XRootWindow(display, XDefaultScreen(display));
    auto pixmap = XCreatePixmap(display, drawable, width, height, 32);

    // Create XRender picture
    XRenderPictureAttributes attr;
    attr.poly_edge = PolyEdgeSmooth;
    attr.poly_mode = PolyModeImprecise;
    auto drawArea = XRenderCreatePicture(display, pixmap, pictFormat,
                                         CPPolyEdge | CPPolyMode, &attr);

    // Clear pixmap to black
    auto bgColor = makeColor(0.0f, 0.0f, 0.0f);
    XRenderFillRectangle(display, PictOpSrc, drawArea, &bgColor, 0, 0, width, height);

    // Draw rectangle with two triangles in white
    float ul[2] = { 2.0f, 2.0f };
    float ur[2] = { 11.0f, 2.0f };
    float ll[2] = { 2.0f, 13.0f };
    float lr[2] = { 11.0f, 13.0f };
    std::vector<XTriangle> tris;
    tris.push_back(makeTri(ll[0], ll[1], lr[0], lr[1], ur[0], ur[1]));
    tris.push_back(makeTri(ll[0], ll[1], ur[0], ur[1], ul[0], ul[1]));

    auto fgColor = makeColor(1.0f, 1.0f, 1.0f);
    auto fg = XRenderCreateSolidFill(display, &fgColor);
    XRenderCompositeTriangles(display, PictOpOver, fg, drawArea, nullptr, 0, 0,
                              tris.data(), tris.size());

    XSync(display, pixmap);
    auto readableCopy = XGetImage(display, pixmap, 0, 0, width, height,
                                  0xffffffff, ZPixmap);
    const char kHex[] = "0123456789abcdef";
    for (int y = 0; y < height; ++y) {
        std::string s = "";
        for (int x = 0; x < width; ++x) {
            unsigned long pixel = XGetPixel(readableCopy, x, y);
            unsigned char a = (pixel & 0xff000000) >> 24;
            unsigned char r = (pixel & 0x00ff0000) >> 16;
            unsigned char g = (pixel & 0x0000ff00) >> 8;
            unsigned char b = (pixel & 0x000000ff);
            s += kHex[int(r / 16)];
            s += kHex[int(g / 16)];
            s += kHex[int(b / 16)];
            s += " ";
        }
        std::cout << s << std::endl;
    }

    XDestroyImage(readableCopy);
    XFreePixmap(display, pixmap);
    XCloseDisplay(display);

    return 0;
}

What am I doing wrong?

x11
xlib
asked on Stack Overflow Jan 16, 2021 by prewett

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0