Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React-Native Android: Resizing native UI Component causes black out

I have a very strange issue I'm struggling with regarding using GLSurfaceView with react native on Android.

I've created a custom OpenGL renderer that I want to use as a component in a larger react native view. The problem is that when I size the component on the JS side to the size I want the rest of the screen goes black.

enter image description here

This is the React Native code that's creating that view:

    public render() {
       return (
            <View style={{ backgroundColor: '#ffffff' }}>
                <Text>Hello World!</Text>
                <ImageBlendView style={{ height: 50 }} boxColour="#f442df" />
            </View>
        );
    }

ImageBlendView is a react-native UI component I made following the instructions on the RN website here, this is what's creating the red with the purple rectangle in it. It is a just a pretty basic Opengl renderer that is drawing a rectangle with the color passed in from JS and a red clear color. I added a text field above it and sized the component to 50-height.

I can't figure out why the ImageBlendView component is surrounded by black.

The clear color is red so I know its not my renderer clearing pixels it shouldn't be. However, it does seem to be related to my renderer class because when I comment out the line in the GLSurfaceView class where I set it as the renderer the normal white background comes back and the text above the component is visible.

// setRenderer(new OpenGLRenderer(_context, colour));

The custom UI component follows the same structure as in the tutorial but I'll walk through it to be thorough.

The ImageBlendView is added to a package and bundled in the view managers list:

public class OTSNativePackage implements ReactPackage {

    @Nonnull
    @Override
    public List<NativeModule> createNativeModules(@Nonnull ReactApplicationContext reactContext) {
        List<NativeModule> nativeModules = new ArrayList<>();
        return nativeModules;
    }

    @Nonnull
    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Arrays.<ViewManager>asList(new ImageBlendView());
    }
}

ImageBlendView.java then extends the SimpleViewManager which creates an instance of OpenGLView which extends GLSurfaceView

public class ImageBlendView extends SimpleViewManager<OpenGLView> {

    public static final String REACT_CLASS = "ImageBlendView";
    private static OpenGLView _openGLView;

    @Nonnull
    @Override
    public String getName() {
        return REACT_CLASS;
    }

    @Nonnull
    @Override
    protected OpenGLView createViewInstance(@Nonnull ThemedReactContext reactContext) {
        _openGLView = new OpenGLView(reactContext);
        return _openGLView;
    }


    @ReactProp(name = "boxColour")
    public void setBoxColour(OpenGLView view, String color) {
        _openGLView.init(color);
    }
}

OpenGLView.java

public class OpenGLView extends GLSurfaceView {
    private Context _context;
    public OpenGLView(Context context) {
        super(context);
        _context = context;
    }

    public OpenGLView(Context context, AttributeSet attrs) {
        super(context, attrs);
        _context = context;
    }

    public void init(String colour){
        setEGLContextClientVersion(2);
        setPreserveEGLContextOnPause(true);

        // If I disable the following line the black goes away
        setRenderer(new OpenGLRenderer(_context, colour)); 
    }
}

Note that it is here in the above class where I can disable the setRenderer line and the black goes away.

My Renderer is in OpenGLRenderer.java

public class OpenGLRenderer implements GLSurfaceView.Renderer {
    private Context _context;
    private FloatBuffer vertexBuffer;
    private ShortBuffer drawListBuffer;
    private int positionHandle, program, colorHandle;
    static final int COORDS_PER_VERTEX = 3;

    static float squareCoords[] = {
        -0.5f,  0.5f, 0.0f,   // top left
        -0.5f, -0.5f, 0.0f,   // bottom left
         0.5f, -0.5f, 0.0f,   // bottom right
         0.5f,  0.5f, 0.0f }; // top right

    private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices


    float color[] = {0.5f, 0.5f, 0.55f, 1.0f};
    private int vertexCount;
    private int vertexStride; // 4 bytes per vertex

    public OpenGLRenderer(Context context, String colorStr){
        super();
        _context = context;
        color = new float[]{(float)Integer.valueOf( colorStr.substring( 1, 3 ), 16 ) / 256.0f,
                        (float)Integer.valueOf( colorStr.substring( 3, 5 ), 16 ) / 256.0f,
                        (float)Integer.valueOf( colorStr.substring( 5, 7 ), 16 ) / 256.0f, 1.0f};
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GLES20.glClearColor(1f,0f,0f,1f);

        ByteBuffer bb = ByteBuffer.allocateDirect(squareCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(squareCoords);
        vertexBuffer.position(0);

        // initialize byte buffer for the draw list
        ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        drawListBuffer = dlb.asShortBuffer();
        drawListBuffer.put(drawOrder);
        drawListBuffer.position(0);

        vertexCount = squareCoords.length / COORDS_PER_VERTEX;
        vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

        GLES20.glEnable(GLES20.GL_SCISSOR_TEST);

        compileShaders();
        sendData();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
        GLES20.glScissor(0,0, width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

        GLES20.glEnableVertexAttribArray(positionHandle);

        GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);

        GLES20.glDisableVertexAttribArray(positionHandle);
    }

    private void sendData(){
        positionHandle = GLES20.glGetAttribLocation(program, "position");
        GLES20.glEnableVertexAttribArray(positionHandle);

        GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX,
                             GLES20.GL_FLOAT, false,
                             vertexStride, vertexBuffer);

        colorHandle = GLES20.glGetUniformLocation(program, "vColor");

        GLES20.glUniform4fv(colorHandle, 1, color, 0);
    }

    private void compileShaders() {

        int vertexShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
        GLES20.glShaderSource(vertexShader, getShaderSource(R.raw.vertex_shader));

        int fragmentShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
        GLES20.glShaderSource(fragmentShader, getShaderSource(R.raw.fragment_shader));

        GLES20.glCompileShader(vertexShader);
        GLES20.glCompileShader(fragmentShader);

        program = GLES20.glCreateProgram();
        GLES20.glAttachShader(program, vertexShader);
        GLES20.glAttachShader(program, fragmentShader);

        GLES20.glLinkProgram(program);
        GLES20.glUseProgram(program);
    }

    private String getShaderSource(int input){
        StringBuilder total = new StringBuilder();
        try{
            InputStream stream =
                _context.getResources().openRawResource(input);

            BufferedReader r = new BufferedReader(new InputStreamReader(stream));
            total = new StringBuilder();
            for (String line; (line = r.readLine()) != null; ) {
                total.append(line).append('\n');
            }
        } catch (IOException e){
            System.out.println(e.getMessage());
        }

        return total.toString();
    }
}

The latest thing I tried was the call to GLES20.glScissor(0,0, width, height); in the onSurfaceChanged method. I was hoping by scissoring to just the size and height of the component it wouldn't affect the rest of the pixels but its becoming pretty clear that its not an issue with the OpenGL but the setup.

like image 637
Nick Avatar asked Jul 19 '26 03:07

Nick


1 Answers

Finally figured out a solution. I simply changed the JS code to this:

    public render() {
        console.log('***', ImageBlend);
        return (
            <View style={{ backgroundColor: '#ffffff' }}>
                <Text>Hello Nick!</Text>
                <View style={{ height: 50, overflow: 'hidden' }}>
                    <ImageBlendView
                        style={{ height: 50 }}
                        boxColour="#f442df"
                    />
                </View>
            </View>
        );
    }
like image 111
Nick Avatar answered Jul 20 '26 18:07

Nick