1 /*
2  *  Copyright 2015 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 package org.webrtc;
12 
13 import android.opengl.GLES20;
14 
15 /**
16  * Helper class for handling OpenGL framebuffer with only color attachment and no depth or stencil
17  * buffer. Intended for simple tasks such as texture copy, texture downscaling, and texture color
18  * conversion. This class is not thread safe and must be used by a thread with an active GL context.
19  */
20 // TODO(magjed): Add unittests for this class.
21 public class GlTextureFrameBuffer {
22   private final int pixelFormat;
23   private int frameBufferId;
24   private int textureId;
25   private int width;
26   private int height;
27 
28   /**
29    * Generate texture and framebuffer resources. An EGLContext must be bound on the current thread
30    * when calling this function. The framebuffer is not complete until setSize() is called.
31    */
GlTextureFrameBuffer(int pixelFormat)32   public GlTextureFrameBuffer(int pixelFormat) {
33     switch (pixelFormat) {
34       case GLES20.GL_LUMINANCE:
35       case GLES20.GL_RGB:
36       case GLES20.GL_RGBA:
37         this.pixelFormat = pixelFormat;
38         break;
39       default:
40         throw new IllegalArgumentException("Invalid pixel format: " + pixelFormat);
41     }
42     this.width = 0;
43     this.height = 0;
44   }
45 
46   /**
47    * (Re)allocate texture. Will do nothing if the requested size equals the current size. An
48    * EGLContext must be bound on the current thread when calling this function. Must be called at
49    * least once before using the framebuffer. May be called multiple times to change size.
50    */
setSize(int width, int height)51   public void setSize(int width, int height) {
52     if (width <= 0 || height <= 0) {
53       throw new IllegalArgumentException("Invalid size: " + width + "x" + height);
54     }
55     if (width == this.width && height == this.height) {
56       return;
57     }
58     this.width = width;
59     this.height = height;
60     // Lazy allocation the first time setSize() is called.
61     if (textureId == 0) {
62       textureId = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D);
63     }
64     if (frameBufferId == 0) {
65       final int frameBuffers[] = new int[1];
66       GLES20.glGenFramebuffers(1, frameBuffers, 0);
67       frameBufferId = frameBuffers[0];
68     }
69 
70     // Allocate texture.
71     GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
72     GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
73     GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, pixelFormat, width, height, 0, pixelFormat,
74         GLES20.GL_UNSIGNED_BYTE, null);
75     GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
76     GlUtil.checkNoGLES2Error("GlTextureFrameBuffer setSize");
77 
78     // Attach the texture to the framebuffer as color attachment.
79     GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBufferId);
80     GLES20.glFramebufferTexture2D(
81         GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, textureId, 0);
82 
83     // Check that the framebuffer is in a good state.
84     final int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
85     if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
86       throw new IllegalStateException("Framebuffer not complete, status: " + status);
87     }
88 
89     GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
90   }
91 
getWidth()92   public int getWidth() {
93     return width;
94   }
95 
getHeight()96   public int getHeight() {
97     return height;
98   }
99 
100   /** Gets the OpenGL frame buffer id. This value is only valid after setSize() has been called. */
getFrameBufferId()101   public int getFrameBufferId() {
102     return frameBufferId;
103   }
104 
105   /** Gets the OpenGL texture id. This value is only valid after setSize() has been called. */
getTextureId()106   public int getTextureId() {
107     return textureId;
108   }
109 
110   /**
111    * Release texture and framebuffer. An EGLContext must be bound on the current thread when calling
112    * this function. This object should not be used after this call.
113    */
release()114   public void release() {
115     GLES20.glDeleteTextures(1, new int[] {textureId}, 0);
116     textureId = 0;
117     GLES20.glDeleteFramebuffers(1, new int[] {frameBufferId}, 0);
118     frameBufferId = 0;
119     width = 0;
120     height = 0;
121   }
122 }
123