Skip to content

RGB mask endianness using is bogus/inconsistent

The i_rmask, i_bmask, i_bmask of the video format don't specify whether they are using native endianness or values meant to match the memory order of the color components.

The default mask for VLC_CODEC_RGB32 is 0x00ff0000, 0x0000ff00, 0x000000ff, according to video_format_FixRGB.

The libavutil code maps it to AV_PIX_FMT_0BGR when compiled on Little-Endian and AV_PIX_FMT_0RGB on Big-Endian:

VLC_RGB( VLC_CODEC_RGB32, AV_PIX_FMT_0RGB, AV_PIX_FMT_0BGR, 0x00ff0000, 0x0000ff00, 0x000000ff)

This suggests that the mask are not supposed to match the memory order.

We have code like GetPackedRgbIndexes that is supposed to give the index in memory order given a chroma+mask. It also gives XBGR/0BGR on Little-Endian for the default mask.

So far, so good.

But there's also the RGB to I420 converter that assumes different things:

    /* If we don't have support for the bitmasks, bail out */
    if( p_filter->fmt_out.video.i_rmask == 0x00ff0000
        && p_filter->fmt_out.video.i_gmask == 0x0000ff00
        && p_filter->fmt_out.video.i_bmask == 0x000000ff )
    {
        /* A8R8G8B8 pixel format */
        msg_Dbg(p_filter, "RGB pixel format is A8R8G8B8");
        p_filter->ops = &I420_A8R8G8B8_ops;
    }

The mask is also used to read/write RGB from AVI and to set the mask in wingdi for RGB. The usage of wingdi can help us find what mask should be written in AVI (BITMAPINFO).

I modified the mock code to set 4 primary colors in 4 vertical division:

#if 1
    uint32_t *pixels = (uint32_t*)pic->p[0].p_pixels;
    unsigned lines_per_color = pic->p[0].i_visible_lines / 4;
    for (unsigned y=0*lines_per_color; y < 1*lines_per_color; y++)
        for (int x=0; x < pic->p[0].i_visible_pitch / 4; x++)
        {
            pixels[x + y*pic->p[0].i_pitch/4] = hton32(0xFF000000);
        }
    for (unsigned y=1*lines_per_color; y < 2*lines_per_color; y++)
        for (int x=0; x < pic->p[0].i_visible_pitch / 4; x++)
        {
            pixels[x + y*pic->p[0].i_pitch/4] = hton32(0x00FF0000);
        }
    for (unsigned y=2*lines_per_color; y < 3*lines_per_color; y++)
        for (int x=0; x < pic->p[0].i_visible_pitch / 4; x++)
        {
            pixels[x + y*pic->p[0].i_pitch/4] = hton32(0x0000FF00);
        }
    for (unsigned y=3*lines_per_color; y < 4*lines_per_color; y++)
        for (int x=0; x < pic->p[0].i_visible_pitch / 4; x++)
        {
            pixels[x + y*pic->p[0].i_pitch/4] = hton32(0x000000FF);
        }
#else
    memset(pic->p[0].p_pixels, pixel, block_len);
#endif

When using this with mock://video_track_count=1;video_chroma=RV32 the default RGB32 mask ends ups showing Blue, Green, Red, Black from top to bottom. This corresponds to BGRX, and not XBGR as expected.

When using mock://video_track_count=1;video_chroma=BGRX and forcing VLC_CODEC_BGRX in wingdi, the proper mask to use is:

    *((DWORD*)&sys->bi_rgb.red)   = hton32(0x0000ff00);
    *((DWORD*)&sys->bi_rgb.green) = hton32(0x00ff0000);
    *((DWORD*)&sys->bi_rgb.blue)  = hton32(0xff000000);

This is the opposite of the masks we are using right now. In bitmapheaderinfo we use

    SetDWBE( &p_bmiColors[0], i_rmask );
    SetDWBE( &p_bmiColors[4], i_gmask );
    SetDWBE( &p_bmiColors[8], i_bmask );

In this case the mask values should not use hton32(), 5c42f091 is wrong.

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information