Is that possible to rendering with viewport non-transparent and other area of window transparentin

Hi Guys,
I’m using opengles library to render video frames download from internet. the window size is 2880*960, I need render in a specific position and window size, here I use glViewport. requirement is I need to make area out of viewport keep transparent and viewport area non-transparent(video frame can not cover full view port).
is this possible?

It’s unclear exactly what you’re trying to achieve, or what some of your description means. Language is apparently a challenge here.

“Transparent” is a somewhat vague term because you could mean:

  • Transparent (is not even drawn!) ← Typically ALPHA = 0.0
  • Translucent (blends with background) ← Typically ALPHA between 0.0 and 1.0
  • Opaque (replaces background) ← Typically ALPHA = 1.0

If what you’re going for is to draw your video frames within the viewport, but then have a sub-rectangle (sub-portion) of that viewport for which you NEVER draw any video frame pixels, then this isn’t really using “transparency” at all. You can use Stencil Test for this. Just clear the STENCIL buffer, set some stencil value in the pixels in the sub-region you want to leave alone, and then when drawing the video frames, enable stencil test and set the test to discard COLOR writes for any pixels that already contain your magic stencil value in the STENCIL buffer. In the end, you’ll have nicely rendered video frames with a sub-rectangle cut out of the middle.

If on-the-other-hand you wanted to draw your video frames within the viewport, but “only” draw them within a sub-rectangle of the viewport, then this isn’t really using “transparency” at all either. Just use Scissor Test to reject everything outside of that sub-rectangle.

1 Like

The function I made is a video player that renders video frames (without changing the video frame aspect ratio) into a specified viewport, which means that there will be a part of the viewport that will not be covered by the video frames, and the size and position of this unmasked area will vary with the aspect ratio of the different videos being rendered.
I need to set the viewport background to a solid black background and render the video on this. At the same time, the area outside the viewport is transparent.
With the current solution I can only do this if the area outside the video render is transparent.
The code is as follows

void OpenglWaylandSink::prepare()
    if (_eglContext)
        glClearColor(0, 0, 0, 0.0);
        eglSwapBuffers(_eglContext->_eglDisplay, _eglContext->_eglSurface);

As shown above, the blue area needs to remain transparent (to ensure that the user can see the rest of the content under this surface) and the red part remains opaque with a black background (the red rectangle is the viewport)

Ok, then it sounds like what you want is to draw like the following.

    // Clear the entire window to transparent (alpha = 0)
    glClearColor( 0, 0, 0, 0 );
    glDisable   ( GL_SCISSOR_TEST );

    // Clear the viewport (subset of window) to opaque black
    glClearColor( 0, 0, 0, 1 );
    glViewport  ( vp_x, vp_y, vp_w, vp_h );
    glScissor   ( vp_x, vp_y, vp_w, vp_h );
    glEnable    ( GL_SCISSOR_TEST );
    glClear     ( GL_COLOR_BUFFER_BIT );

    // Draw video from inset within viewport
    glViewport( video_x, video_y, video_w, video_h );
    glScissor ( video_x, video_y, video_w, video_h );
    glDisable ( GL_BLEND );
    glDraw... ( ... );

    // Display frame
    eglSwapBuffers( _eglContext->_eglDisplay, _eglContext->_eglSurface );

Based on your description, you don’t need blending (at least in your app’s GL rendering).

If you just want to clear the window to (0,0,0,0), then most of this is irrelevant. In my rewrite above, I’ve deleted the API calls unnecessary for the clear operation.

Yes, You are correct, I don’t blending function, I remove relevant API calling, window is still transparent.
prepare() is function will be call at the beginning, I change it to

    if (_eglContext)
        // Clear the entire window to transparent (alpha = 0)
        glClearColor( 0, 0, 0, 0 );
        glDisable( GL_SCISSOR_TEST );

the behavior is same with before, then I tried to update update_window() as below:

    if(config.get_mask() & VideoSinkConfiguration::EViewPort)
        isWindowChanged = true;
        _wlContext->_settings._viewport = config.get_view_port();

        streambase::Size viewportSize = config.get_view_port()._size;
        if (viewportSize._height == 0 && viewportSize._width == 0)
            viewportSize = get_screen_size();
            _wlContext->_settings._viewport._size = viewportSize;
        streambase::Position viewportPos = config.get_view_port()._pos;
        glClearColor(1, 0, 0, 1);
        glViewport(viewportPos._x, viewportPos._y, viewportSize._width, viewportSize._height);
        glScissor(viewportPos._x, viewportPos._y, viewportSize._width, viewportSize._height);

default player will render video stream in full screen mode, it ok with red background(for test )
but when i change view port. In the viewport area, the rendering is what I want, but in the area outside the viewport, instead of being transparent, it’s a splash screen (like a mosaic) and I can see small grids of images that move as the video plays in the viewport

void render_frame(const VideoFramePtr& frame)
    byte* i420PackedData = nullptr;
    byte* y_data = nullptr;
    byte* u_data = nullptr;
    byte* v_data = nullptr;

    unsigned ystride = frame->strides()[streambase::EYPlane];
    unsigned ustride = frame->strides()[streambase::EUPlane];
    unsigned vstride = frame->strides()[streambase::EVPlane];

    // already I420 Packed yuv
    if(ystride == width && ustride == width/2 && vstride == width/2)
        y_data = frame->plane_data(streambase::EYPlane);
        u_data = frame->plane_data(streambase::EUPlane);
        v_data = frame->plane_data(streambase::EVPlane);
    else // convert to I420 packed yuv
        i420PackedData = frame->convert_to_I420_packed_for_render();
        y_data = i420PackedData;
        u_data = y_data + width * height;
        v_data = u_data + width * height / 4;

    PlaneInfo yData = {y_data, width, height};
    PlaneInfo uData = {u_data, width / 2, height / 2};
    PlaneInfo vData = {v_data, width / 2, height / 2};

    if (!_isTexturesCreated)
        // Fixes abnormality with for example 486x864 yuv data
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

        _isTexturesCreated = create_texture(yData, uData, vData);
        if (!_isTexturesCreated)
            LOG_ERROR() << "create yuv textures failed";

    PlaneInfo buf[3] = {yData, uData, vData};
    for (int i = 0; i < 3; i++)
        PlaneInfo data = buf[i];
        glActiveTexture(GL_TEXTURE0 + i);
        glBindTexture(GL_TEXTURE_2D, _glesContext->_textureIds[i]);
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, data._width, data._height, GL_LUMINANCE, GL_UNSIGNED_BYTE, data._data);

    if (_isOrthoMatChanged)
        glUniformMatrix4fv(_glesContext->_uMatrix, 1, 0U, (GLfloat*)&_glesContext->_mat4V);
        _isOrthoMatChanged = false;

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    eglSwapBuffers(_eglContext->_eglDisplay, _eglContext->_eglSurface);

and one more question about you last reply,
why should I call

    glViewport( video_x, video_y, video_w, video_h );
    glScissor ( video_x, video_y, video_w, video_h );

again before rendering frame(I mean it have call them in step Clear the viewport (subset of window) to opaque black)?

Sounds like you’ve got a bug to chase down here. Possibly viewportPos above is wrong, or your driver doesn’t properly implement framebuffer clears with scissor test enabled.

Because, per your picture above, the “VIEWPORT” rectangle (opaque RED in your diagram)
) != your “VIDEO” rectangle. The latter is a subset of the former.

You said you wanted your VIEWPORT rectangle’s background to be “solid black”. Thus the opaque black clear color on that 2nd glClear() call.

1 Like

What I can confirm is that the viewportPos is correct and I’m not sure if it’s a problem with the driver.
Thanks for your reply. for now, I can get the answer of “it’s possible” at least

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.