Displaying texture from vector of bytes

I’d like to display a vector of bytes in a bgr888 format in an sdl window with minimal copies and without looping on every pixel.

If I create a texture with SDL_PixelFormatBGR888 then update the texture with the vector .data() and image step/pitch size I see colors bands, there is a column of green pixels, then red then blue. The values I set can make the color bands lighter or darker only.

If I create the texture with SDL_PixelFormatBGRA8888 I can create a test pattern in the bytes that appears as I expect without the color bands.

I was expecting the BGR888 texture to get converted under the hood for free or at least much quicker than if I made a for loop that manually copied 3 bgr bytes from the incoming vector and assigned them to the 4 bgra bytes, maybe I’m missing something that can cause this to happen? Or is it that BGR888 is not a blue byte followed by a green then red byte and repeat for every pixel in every row, and every row in the height, some other layout is in use?

buffer = SDL_CreateTexture(
    ren_,
    // TODO(lucasw) need to match on the msg->encoding
    // This doesn't display correctly
    SDL_PIXELFORMAT_BGR888,
    // SDL_PIXELFORMAT_BGRA8888,
    // SDL_TEXTUREACCESS_STREAMING,
    SDL_TEXTUREACCESS_STATIC,
    msg->width,
    msg->height);

std::vector<Uint8> pixels;
pixels.resize(msg->step * msg->height);
for (size_t i = 0; i < pixels.size() - 3; i+=3) {
// for (size_t i = 0; i < pixels.size() - 4; i+=4) {
  // test pattern
  pixels[i] = (i % msg->step) % 255;
  pixels[i + 1] = 0xff;
  pixels[i + 2] = (i / msg->step) % 255;
  // pixels[i + 3] = 0xff;
}

SDL_Rect msg_rect;
msg_rect.x = 0;
msg_rect.y = 0;
msg_rect.w = msg->width;
msg_rect.h = msg->height;
// This seg faults unless 50 lines of padding are added to the texture
// SDL_UpdateTexture(buffer_, &msg_rect, msg->data.data(), msg->step);
const int rv0 = SDL_UpdateTexture(buffer, &msg_rect, pixels.data(), msg->step);
if (rv0 != 0) {
  ROS_ERROR_STREAM("sdl update texture failed: " << SDL_GetError());
  return;
}
SDL_Rect dst_rect;
dst_rect.x = 0;
dst_rect.y = 0;
dst_rect.w = std::min(msg->width, width);
dst_rect.h = std::min(msg->height, height);
// ROS_INFO_STREAM("updated " << dst_rect.w << " " << dst_rect.h);
SDL_Rect src_rect = dst_rect;
const int rv1 = SDL_RenderCopy(ren_, buffer, &src_rect, &dst_rect);
if (rv1 != 0) {
  ROS_ERROR_STREAM("sdl render copy failed: " << SDL_GetError());
  return;
}
SDL_RenderPresent(ren_);

The source code is here sdl2_ros/view_image.cpp at master · lucasw/sdl2_ros · GitHub

SDL_PIXELFORMAT_BGR888 is 4 bytes per pixel (the alpha byte is ignored and treated as full alpha), which is unexpected.

–ryan.

1 Like

Ok. I went with creating a SDL_Surface from a pointer to bgr bytes instead and that worked as expected (and performance seemed good).

1 Like