Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 1 | /* |
kjellander | b24317b | 2016-02-10 07:54:43 -0800 | [diff] [blame] | 2 | * Copyright 2015 The WebRTC project authors. All Rights Reserved. |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 3 | * |
kjellander | b24317b | 2016-02-10 07:54:43 -0800 | [diff] [blame] | 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. |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 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. |
| 19 | */ |
| 20 | // TODO(magjed): Add unittests for this class. |
| 21 | public class GlTextureFrameBuffer { |
| 22 | private final int frameBufferId; |
| 23 | private final int textureId; |
| 24 | private final int pixelFormat; |
| 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 | */ |
| 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 | |
magjed | 9b96a17 | 2017-01-17 02:51:06 -0800 | [diff] [blame] | 43 | // Create texture. |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 44 | textureId = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D); |
| 45 | this.width = 0; |
| 46 | this.height = 0; |
| 47 | |
magjed | 9b96a17 | 2017-01-17 02:51:06 -0800 | [diff] [blame] | 48 | // Create framebuffer object. |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 49 | final int frameBuffers[] = new int[1]; |
| 50 | GLES20.glGenFramebuffers(1, frameBuffers, 0); |
| 51 | frameBufferId = frameBuffers[0]; |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 52 | } |
| 53 | |
| 54 | /** |
| 55 | * (Re)allocate texture. Will do nothing if the requested size equals the current size. An |
| 56 | * EGLContext must be bound on the current thread when calling this function. Must be called at |
| 57 | * least once before using the framebuffer. May be called multiple times to change size. |
| 58 | */ |
| 59 | public void setSize(int width, int height) { |
| 60 | if (width == 0 || height == 0) { |
| 61 | throw new IllegalArgumentException("Invalid size: " + width + "x" + height); |
| 62 | } |
| 63 | if (width == this.width && height == this.height) { |
| 64 | return; |
| 65 | } |
| 66 | this.width = width; |
| 67 | this.height = height; |
| 68 | |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 69 | // Allocate texture. |
| 70 | GLES20.glActiveTexture(GLES20.GL_TEXTURE0); |
| 71 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); |
| 72 | GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, pixelFormat, width, height, 0, pixelFormat, |
| 73 | GLES20.GL_UNSIGNED_BYTE, null); |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 74 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); |
sakal | 1c82884 | 2016-11-23 06:12:26 -0800 | [diff] [blame] | 75 | GlUtil.checkNoGLES2Error("GlTextureFrameBuffer setSize"); |
magjed | 9b96a17 | 2017-01-17 02:51:06 -0800 | [diff] [blame] | 76 | |
| 77 | // Attach the texture to the framebuffer as color attachment. |
| 78 | GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBufferId); |
| 79 | GLES20.glFramebufferTexture2D( |
| 80 | GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, textureId, 0); |
| 81 | |
| 82 | // Check that the framebuffer is in a good state. |
| 83 | final int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER); |
| 84 | if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) { |
| 85 | throw new IllegalStateException("Framebuffer not complete, status: " + status); |
| 86 | } |
| 87 | |
| 88 | GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 89 | } |
| 90 | |
| 91 | public int getWidth() { |
| 92 | return width; |
| 93 | } |
| 94 | |
| 95 | public int getHeight() { |
| 96 | return height; |
| 97 | } |
| 98 | |
| 99 | public int getFrameBufferId() { |
| 100 | return frameBufferId; |
| 101 | } |
| 102 | |
| 103 | public int getTextureId() { |
| 104 | return textureId; |
| 105 | } |
| 106 | |
| 107 | /** |
| 108 | * Release texture and framebuffer. An EGLContext must be bound on the current thread when calling |
| 109 | * this function. This object should not be used after this call. |
| 110 | */ |
| 111 | public void release() { |
| 112 | GLES20.glDeleteTextures(1, new int[] {textureId}, 0); |
| 113 | GLES20.glDeleteFramebuffers(1, new int[] {frameBufferId}, 0); |
| 114 | width = 0; |
| 115 | height = 0; |
| 116 | } |
| 117 | } |