14

I'm having trouble displaying an image (PNG extracted with libpng) into an XCB window, it is always entirely empty/white. I'm pretty sure the PNG extraction is correct since I can perfectly re-write it into another file.

I've tried everything I found (explanations, guides, documentation) and I'm running out of ideas:

  • Creating an xcb_pixmap_t calling xcb_create_pixmap_from_bitmap_data() with the data taken from the PNG, then calling xcb_copy_area() into the EXPOSE part of the event loop.
  • Creating an xcb_image_t* calling xcb_image_create_from_bitmap_data() then trying to map it to the window with xcb_image_put(). I've even tried to display each pixel with xcb_image_put_pixel(), but without success.

Code sample:

  xcb_pixmap_t pixmap = xcb_create_pixmap_from_bitmap_data(
                            connection, // xcb_connect(0, 0)    (type: xcb_connection_t*)
                            window, // xcb_generate_id(connection)    (type: xcb_window_t)
                            img.getData(), // uint8_t*
                            img.getWidth(), // 128
                            img.getHeight(), // 128
                            img.getBitDepth(), // 8
                            screen->black_pixel, // screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data    (type: xcb_screen_t*)
                            screen->white_pixel,
                            nullptr);

   // "img" is an instance of my own custom class, result of PNG reading
  xcb_image_t* image = xcb_image_create_from_bitmap_data(
                              img.getData(),
                              img.getWidth(),
                              img.getHeight()); // image->data seems fine

  xcb_image_put(connection,
                window,
                graphicsContext,
                image, 0, 0, 0); // This does nothing

  for (unsigned int i = 0; i < screen->height_in_pixels; ++i)
    for (unsigned int j = 0; j < screen->width_in_pixels; ++j)
      xcb_image_put_pixel(image, j, i, 0); // Displays nothing

  [...]

  // Into event loop
  case XCB_EXPOSE: {
    xcb_expose_event_t* exposeEvent = reinterpret_cast<xcb_expose_event_t*>(event);
    xcb_copy_area(connection,
                  pixmap,
                  window,
                  graphicsContext,
                  exposeEvent->x, exposeEvent->y, // Top left x & y coordinates of the source's region to copy
                  exposeEvent->x, exposeEvent->y, // Top left x & y coordinates of the destination's region to copy to
                  exposeEvent->width,
                  exposeEvent->height);
    xcb_flush(connection);
    break;
  }

From the examples I found I saw that it didn't need a colormap, but could that be the case? Could anyone tell me where I've gone wrong?

luser droog
  • 17,793
  • 3
  • 49
  • 96
Razakhel
  • 715
  • 10
  • 30
  • What kind of trouble? Please be as specific as possible. – n. 'pronouns' m. Mar 28 '17 at 14:29
  • `img.getBitDepth(), // 8` this could be your problem. The depth must be the same as your window depth, or `xcb_copy_area` will fail. Do you really have 8-bit deep windows by default? In general you should be prepared to handle all depths. – n. 'pronouns' m. Mar 28 '17 at 14:35
  • @n.m. Thanks for your answer! Good point, I edited to specify what was wrong, silly me. I actually don't know which depth my window has, I'm passing `XCB_COPY_FROM_PARENT` to the window while creating it. By replacing this field with `8`, the window does not even appear. – Razakhel Mar 28 '17 at 16:04
  • You need to know what your target window depth is, in bits, *and* you need to convert your image data to that specific bit depth. It helps if you know your source image data representation, otherwise you won't be able to convert. A no-op conversion a.k.a. is just one of many possible cases. – n. 'pronouns' m. Mar 28 '17 at 16:20
  • Actually this is a pretty good start. My window had 24 bit depth (that seems huge since my PNG always has 8), I've expanded PNG's depth to 24 but it now displays an ugly square made of artifacts. I guess there's another thing that's going wrong... Is there a way to downgrade window's depth to 8 or 16? – Razakhel Mar 28 '17 at 19:05
  • "my PNG always has 8". This doesn't sound right to me at all. "Is there a way to downgrade window's depth to 8 or 16" You don't want this. – n. 'pronouns' m. Mar 28 '17 at 19:09
  • OK sorry about the confusion. I'm an XLib guy, not xcb. I have actually looked in the xcb manual. `xcb_create_pixmap_from_bitmap_data` doesn't do what you think it does. It is for source data of depth of **one** bit. Same thing about `xcb_image_create_from_bitmap_data`. It's for **one** bit deep bitmaps. You want `xcb_image_create` only. This is an intimidating function. Look up usage examples on the net before trying to call it. – n. 'pronouns' m. Mar 28 '17 at 19:16
  • To be fair I'm kinda giving up. I've tried using `xcb_image_create` (which I already tried earlier if I recall correctly) and I keep getting a segfault. I've been on it for two days already, just because XCB isn't able to make a proper documentation. I think I'll simply make GLX windows (I'll need OpenGL later anyway). – Razakhel Mar 28 '17 at 21:09
  • You are calling `xcb_image_put` on `window`. This will put the image to the screen if the window is mapped or not at all if the window is not mapped. You will also loose anything put to the window when it is partly covered by an other window or unmaped. Instead you probably want to call `xcb_image_put` on the `pixmap` you created. The pixmap have a permanent buffer and you can from there copy the data to the window whenever the window is exposed (witch you already do). – Rickard Närström May 06 '20 at 14:10

1 Answers1

1

I threw together a simple xcb image viewer about 4 years ago, but just noticed this question, so apologies for the necromancy.

It uses xcb_image, stb_image and nanosvg, but compiles to a relatively small static binary (with a musl or uclibc toolchain)

#include <xcb/xcb.h>
#include <xcb/xcb_image.h>
#define STBI_NO_HDR
#define STBI_NO_LINEAR
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define NANOSVG_IMPLEMENTATION
#include "nanosvg.h"
#define NANOSVGRAST_IMPLEMENTATION
#include "nanosvgrast.h"

int main(int argc, char **argv){
   xcb_connection_t *c = xcb_connect(0, 0);
   xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
   int w, h, n,
      depth = s->root_depth,
      win_class = XCB_WINDOW_CLASS_INPUT_OUTPUT,
      format = XCB_IMAGE_FORMAT_Z_PIXMAP;
   xcb_colormap_t colormap = s->default_colormap;
   xcb_drawable_t win = xcb_generate_id(c);
   xcb_gcontext_t gc = xcb_generate_id(c);
   xcb_pixmap_t pixmap = xcb_generate_id(c);
   xcb_generic_event_t *ev;
   xcb_image_t *image;
   NSVGimage *shapes = NULL;
   NSVGrasterizer *rast = NULL;
   char *data = NULL;
   unsigned *dp;
   size_t i, len;
   uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
      value_mask = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS,
      values[] = { s->black_pixel, value_mask };

   if (argc<2) return -1;
   if ((data = stbi_load(argv[1], &w, &h, &n, 4)))
      ;
   else if ((shapes = nsvgParseFromFile(argv[1], "px", 96.0f))) {
      w = (int)shapes->width;
      h = (int)shapes->height;
      rast = nsvgCreateRasterizer();
      data = malloc(w*h*4);
      nsvgRasterize(rast, shapes, 0,0,1, data, w, h, w*4);
   }else return -1;
   for(i=0,len=w*h,dp=(unsigned *)data;i<len;i++) //rgba to bgra
      dp[i]=dp[i]&0xff00ff00|((dp[i]>>16)&0xFF)|((dp[i]<<16)&0xFF0000);
   xcb_create_window(c,depth,win,s->root,0,0,w,h,1,win_class,s->root_visual,mask,values);
   xcb_create_pixmap(c,depth,pixmap,win,w,h);
   xcb_create_gc(c,gc,pixmap,0,NULL);
   image = xcb_image_create_native(c,w,h,format,depth,data,w*h*4,data);
   xcb_image_put(c, pixmap, gc, image, 0, 0, 0);
   xcb_image_destroy(image);
   xcb_map_window(c, win);
   xcb_flush(c);
   while ((ev = xcb_wait_for_event(c))) {
      switch (ev->response_type & ~0x80){
      case XCB_EXPOSE: {
         xcb_expose_event_t *x = (xcb_expose_event_t *)ev;
         xcb_copy_area(c,pixmap,win,gc,x->x,x->y,x->x,x->y,x->width,x->height);
         xcb_flush(c);
      }break;
      case XCB_BUTTON_PRESS: goto end;
      default: break;
      }
   }
end:
   xcb_free_pixmap(c, pixmap);
   xcb_disconnect(c);
   return 0;
}
technosaurus
  • 7,101
  • 1
  • 28
  • 49
  • Where is the allocated data freed? does xcb_image_destroy() or xcb_free_pixmap() call free() on the pointer you supplied? – Pablo Ariel Mar 29 '21 at 17:22