content: /*
   * Copyright 2014 Google Inc. All rights reserved.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.fadcam.opengl.grafika;
  
  import android.opengl.GLES11Ext;
  import android.opengl.GLES20;
  import android.util.Log;
  
  import java.nio.FloatBuffer;
  
  /**
   * GL program and supporting functions for drawing 2D shapes with textures.
   */
  public class Texture2dProgram {
      private static final String TAG = GlUtil.TAG;
  
      // Simple vertex shader, used for all programs.
      private static final String VERTEX_SHADER =
              "uniform mat4 uMVPMatrix;\n" +
              "uniform mat4 uTexMatrix;\n" +
              "attribute vec4 aPosition;\n" +
              "attribute vec4 aTextureCoord;\n" +
              "varying vec2 vTextureCoord;\n" +
              "void main() {\n" +
              "    gl_Position = uMVPMatrix * aPosition;\n" +
              "    vTextureCoord = (uTexMatrix * aTextureCoord).xy;\n" +
              "}\n";
  
      // Simple fragment shader for use with "normal" 2D textures.
      private static final String FRAGMENT_SHADER_2D =
              "precision mediump float;\n" +
              "varying vec2 vTextureCoord;\n" +
              "uniform sampler2D sTexture;\n" +
              "void main() {\n" +
              "    gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
              "}\n";
  
      // Simple fragment shader for use with external 2D textures (e.g. what we get from
      // SurfaceTexture).
      private static final String FRAGMENT_SHADER_EXT =
              "#extension GL_OES_EGL_image_external : require\n" +
              "precision mediump float;\n" +
              "varying vec2 vTextureCoord;\n" +
              "uniform samplerExternalOES sTexture;\n" +
              "uniform float uExposureCompensation;\n" +
              "void main() {\n" +
              "    vec4 color = texture2D(sTexture, vTextureCoord);\n" +
              "    color.rgb *= pow(2.0, uExposureCompensation);\n" +
              "    gl_FragColor = color;\n" +
              "}\n";
  
      public enum ProgramType {
          TEXTURE_2D, TEXTURE_EXT
      }
  
      // Handles to the GL program and various components of it.
      private int mProgramHandle;
      private int muMVPMatrixLoc;
      private int muTexMatrixLoc;
      private int muExposureCompensationLoc;
      private int maPositionLoc;
      private int maTextureCoordLoc;
  
      private int mTextureTarget;
  
      /**
       * Prepares the program in the current EGL context.
       */
      public Texture2dProgram(ProgramType programType) {
          switch (programType) {
              case TEXTURE_2D:
                  mProgramHandle = GlUtil.createProgram(VERTEX_SHADER, FRAGMENT_SHADER_2D);
                  mTextureTarget = GLES20.GL_TEXTURE_2D;
                  break;
              case TEXTURE_EXT:
                  mProgramHandle = GlUtil.createProgram(VERTEX_SHADER, FRAGMENT_SHADER_EXT);
                  mTextureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES;
                  break;
              default:
                  throw new RuntimeException("Unhandled type " + programType);
          }
          if (mProgramHandle == 0) {
              throw new RuntimeException("Unable to create program");
          }
          Log.d(TAG, "Created program " + mProgramHandle + " (" + programType + ")");
  
          // get locations of attributes and uniforms
          maPositionLoc = GLES20.glGetAttribLocation(mProgramHandle, "aPosition");
          GlUtil.checkLocation(maPositionLoc, "aPosition");
          maTextureCoordLoc = GLES20.glGetAttribLocation(mProgramHandle, "aTextureCoord");
          GlUtil.checkLocation(maTextureCoordLoc, "aTextureCoord");
          muMVPMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uMVPMatrix");
          GlUtil.checkLocation(muMVPMatrixLoc, "uMVPMatrix");
          muTexMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uTexMatrix");
          GlUtil.checkLocation(muTexMatrixLoc, "uTexMatrix");
          
          // Get exposure compensation uniform location (only for TEXTURE_EXT shaders)
          muExposureCompensationLoc = GLES20.glGetUniformLocation(mProgramHandle, "uExposureCompensation");
          if (muExposureCompensationLoc >= 0) {
              Log.d(TAG, "Found exposure compensation uniform at location: " + muExposureCompensationLoc);
          }
      }
  
      /**
       * Releases the program.
       * <p>
       * The appropriate EGL context must be current (i.e. the one that was used to create
       * the program).
       */
      public void release() {
          Log.d(TAG, "deleting program " + mProgramHandle);
          GLES20.glDeleteProgram(mProgramHandle);
          mProgramHandle = -1;
      }
  
      /**
       * Creates a texture object suitable for use with this program.
       * <p>
       * On exit, the texture will be bound.
       */
      public int createTextureObject() {
          int[] textures = new int[1];
          GLES20.glGenTextures(1, textures, 0);
          GlUtil.checkGlError("glGenTextures");
  
          int texId = textures[0];
          GLES20.glBindTexture(mTextureTarget, texId);
          GlUtil.checkGlError("glBindTexture " + texId);
  
          GLES20.glTexParameterf(mTextureTarget, GLES20.GL_TEXTURE_MIN_FILTER,
                  GLES20.GL_NEAREST);
          GLES20.glTexParameterf(mTextureTarget, GLES20.GL_TEXTURE_MAG_FILTER,
                  GLES20.GL_LINEAR);
          GLES20.glTexParameteri(mTextureTarget, GLES20.GL_TEXTURE_WRAP_S,
                  GLES20.GL_CLAMP_TO_EDGE);
          GLES20.glTexParameteri(mTextureTarget, GLES20.GL_TEXTURE_WRAP_T,
                  GLES20.GL_CLAMP_TO_EDGE);
          GlUtil.checkGlError("glTexParameter");
  
          return texId;
      }
  
      /**
       * Issues a GL draw command for a strip of triangles.
       *
       * @param mvpMatrix The model/view/projection matrix.
       * @param vertexBuffer A float buffer with the vertex data.
       * @param firstVertex Which vertex to start drawing with.
       * @param vertexCount How many vertices to draw.
       * @param coordsPerVertex The number of values per vertex (e.g. 2 is x,y; 3 is x,y,z).
       * @param vertexStride The number of bytes per vertex.
       * @param texMatrix A matrix for transforming texture coordinates.
       * @param texBuffer A float buffer with the texture coordinate data.
       * @param textureId The texture to use.
       */
      public void draw(float[] mvpMatrix, FloatBuffer vertexBuffer, int firstVertex,
              int vertexCount, int coordsPerVertex, int vertexStride,
              float[] texMatrix, FloatBuffer texBuffer, int textureId) {
          GlUtil.checkGlError("draw start");
  
          // Select the program.
          GLES20.glUseProgram(mProgramHandle);
          GlUtil.checkGlError("glUseProgram");
  
          // Set the texture.
          GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
          GLES20.glBindTexture(mTextureTarget, textureId);
  
          // Copy the model / view / projection matrix over.
          GLES20.glUniformMatrix4fv(muMVPMatrixLoc, 1, false, mvpMatrix, 0);
          GlUtil.checkGlError("glUniformMatrix4fv");
  
          // Copy the texture transformation matrix over.
          GLES20.glUniformMatrix4fv(muTexMatrixLoc, 1, false, texMatrix, 0);
          GlUtil.checkGlError("glUniformMatrix4fv");
  
          // Enable the "aPosition" vertex attribute.
          GLES20.glEnableVertexAttribArray(maPositionLoc);
          GlUtil.checkGlError("glEnableVertexAttribArray");
  
          // Connect vertexBuffer to "aPosition".
          GLES20.glVertexAttribPointer(maPositionLoc, coordsPerVertex,
              GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
          GlUtil.checkGlError("glVertexAttribPointer");
  
          // Enable the "aTextureCoord" vertex attribute.
          GLES20.glEnableVertexAttribArray(maTextureCoordLoc);
          GlUtil.checkGlError("glEnableVertexAttribArray");
  
          // Connect texBuffer to "aTextureCoord".
          GLES20.glVertexAttribPointer(maTextureCoordLoc, 2,
                  GLES20.GL_FLOAT, false, 0, texBuffer);
          GlUtil.checkGlError("glVertexAttribPointer");
  
          // Draw the rect.
          GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, firstVertex, vertexCount);
          GlUtil.checkGlError("glDrawArrays");
  
          // Done -- disable vertex array, texture, and program.
          GLES20.glDisableVertexAttribArray(maPositionLoc);
          GLES20.glDisableVertexAttribArray(maTextureCoordLoc);
          GLES20.glBindTexture(mTextureTarget, 0);
          GLES20.glUseProgram(0);
      }
  
      /**
       * Returns the exposure compensation uniform location.
       * Returns -1 if the uniform is not found in the shader.
       */
      public int getExposureCompensationLocation() {
          return muExposureCompensationLoc;
      }
  
      /**
       * Returns the program handle for advanced OpenGL operations.
       */
      public int getProgramHandle() {
          return mProgramHandle;
      }
  } 