Search code examples
c++linuxcairo

Difference color when drawing image in Cairo


i have an issue when using Cairo to draw image in linux system. the output image color not similar to original image if using cairo_image_surface_create method. like code below:

int width, height, channels;
unsigned char* data = stbi_load(imagePath.c_str(), &width, &height, &channels, STBI_rgb_alpha);
this->imageSource = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
unsigned char * surface_data = cairo_image_surface_get_data(this->imageSource);
memcpy(surface_data, data, width * height * 4 * sizeof(unsigned char));
cairo_surface_mark_dirty(this->imageSource);
free(data);

But the color will the same when using cairo_image_surface_create_from_png (this api only support png format). see my code as below:

this->imageSource = cairo_image_surface_create_from_png(imagePath.c_str());

Also have a look on attach image to for clearly.

enter image description here


Solution

  • I am pretty sure that the red and blue channels are swapped on that right image. Sadly, I have no idea what STBI_rgb_alpha is and Google does not find anything that looks like helpful docs, so:

    • Cairo stores each pixel as a uint32_t with alpha in the high byte and blue in the low byte, i.e. uint32_t pixel_value = alpha << 24 | red << 16 | green << 8 | blue;.
    • This means that the actual order of the bytes in memory depends on the system endianess.
    • I would guess that STBI_rgb_alpha means that the values are saved byte-wise, independent of the system endian.
    • Thus, cairo would expect byte in the order b, g, r, a while stbi uses r, g, b, a.
    • If you now cycle the bytes to the right by one position, you would end up with red and blue swapped.

    I also find the link by user @Bob__ in the comments really helpful:

    Are you sure that the channels are stored in the expected order? See e.g. http://en.m.wikipedia.org/wiki/RGBA_color_model#Representation