freetypexcb

xcb unable to display freetype bitmap


I am trying to use freetype to rasterize a 8-bit depth bitmap and render it on a 8-bit depth pixmap in xcb.

Everything works as accordingly until I try to call xcb_put_image_checked(). The function returns an error code of 16 (Length), a major code of 72 (PutImage) and a minor code of 0. From what I have gathered the error means the length of the bitmap buffer I am passing to the function is incorrect. After lots of testing I have printed the buffer out byte for byte and manually converted the data to a bitmap and the bitmap comes out correct with the size I have specified to the function. There is no padding in the bitmap that could change the size as bitmap.pitch = 7, bitmap.rows = 7, and bitmap.width = 7. The depth of the pixmap and bitmap match. I am confused as to why xcb_put_image_checked() fails. I have also tried (ft_bitmap.width*ft_bitmap.rows)-1 for the size too.

Here is my code that generates the error (sorry its sloppy)

void testing() {
    FT_Library library;
    char *fonts[] = {"DejaVuSans"};

    FT_Init_FreeType(&library);
    
    /* custom function that loads faces from font config patterns proven to work in this instance */
    xcb_drw_face_t *ft = xcb_drw_faces_load(library,fonts,1);
    if (!ft) {
        logmsg(INFO,"could not load face");
    }

    FT_Load_Char(ft->face,'a',FT_LOAD_RENDER|FT_LOAD_FORCE_AUTOHINT);
    FT_Bitmap ft_bitmap = ft->face->glyph->bitmap;

    logmsg(INFO,"pitch = %i | rows = %i | width = %i",ft_bitmap.pitch,ft_bitmap.rows,ft_bitmap.width); /* pitch = 7 | rows = 7 | width = 7 */

    for (size_t i = 0; i < ft_bitmap.rows*ft_bitmap.width; i++) {
        logmsg(INFO, "value = %i | index = %i",ft_bitmap.buffer[i],i);
    }

    xcb_void_cookie_t cookie;
    xcb_generic_error_t *error;
    xcb_pixmap_t pixmap = xcb_generate_id(c);
    cookie = xcb_create_pixmap_checked(c,8,pixmap,screen.data->root,screen.data->width_in_pixels,screen.data->height_in_pixels);
    if (xcb_request_check(c,cookie)) {
        logmsg(INFO,"could not create pixmap");
    }

    xcb_gcontext_t gc = xcb_generate_id(c);
    cookie = xcb_create_gc_checked(c,gc,pixmap,0,0);
    if (xcb_request_check(c,cookie)) {
        logmsg(INFO,"could not create gc");
    }

    cookie = xcb_put_image_checked(c,XCB_IMAGE_FORMAT_Z_PIXMAP,pixmap,gc,ft_bitmap.width,ft_bitmap.rows,0,0,0,8,ft_bitmap.width*ft_bitmap.rows,ft_bitmap.buffer);
    if ((error = xcb_request_check(c,cookie))) {
        logmsg(INFO,"could not put image on pixmap | err %i | maj %i | min %i",error->error_code,error->major_code,error->minor_code);
    }

    xcb_drw_faces_free(ft);
    xcb_free_pixmap(c,pixmap);
    xcb_free_gc(c,gc);
    FT_Done_FreeType(library);
    return;
}

Here is the image I manually converted - enter image description here


Solution

  • This comes from a requirement from your X11 server. Here is a snippet of output from xdpyinfo:

    supported pixmap formats:
        depth 1, bits_per_pixel 1, scanline_pad 32
        depth 4, bits_per_pixel 8, scanline_pad 32
        depth 8, bits_per_pixel 8, scanline_pad 32
        depth 15, bits_per_pixel 16, scanline_pad 32
        depth 16, bits_per_pixel 16, scanline_pad 32
        depth 24, bits_per_pixel 32, scanline_pad 32
        depth 32, bits_per_pixel 32, scanline_pad 32
    

    The scanline_pad of 32 here is the four bytes of padding you are seeing.

    This is (a bit) explained in https://www.x.org/releases/X11R7.6/doc/xproto/x11protocol.html#id2625443:

    Each scanline is padded to a multiple of bits as given by bitmap-scanline-pad. The pad bits are of arbitrary value. The scanline is quantized in multiples of bits as given by bitmap-scanline-unit. The bitmap-scanline-unit is always less than or equal to the bitmap-scanline-pad. Within

    (To be honest, I think I never saw an X11 server not requiring a padding to a multiple of four bytes.)