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 io.agora.api.example.common.gles.core;
  
  import android.graphics.Bitmap;
  import android.opengl.GLES11Ext;
  import android.opengl.GLES20;
  import android.opengl.GLES30;
  import android.opengl.GLUtils;
  import android.opengl.Matrix;
  import android.util.Log;
  
  import java.nio.ByteBuffer;
  import java.nio.ByteOrder;
  import java.nio.FloatBuffer;
  
  /**
   * Some OpenGL utility functions.
   */
  public abstract class GlUtil {
      //public static final String TAG = "Grafika";
      public static final String TAG = "mqi";
      /**
       * Identity matrix for general use.  Don't modify or life will get weird.
       */
      public static final float[] IDENTITY_MATRIX;
  
      static {
          IDENTITY_MATRIX = new float[16];
          Matrix.setIdentityM(IDENTITY_MATRIX, 0);
      }
  
      private static final int SIZEOF_FLOAT = 4;
  
  
      private GlUtil() {
      }     // do not instantiate
  
      /**
       * Creates a new program from the supplied vertex and fragment shaders.
       *
       * @return A handle to the program, or 0 on failure.
       */
      public static int createProgram(String vertexSource, String fragmentSource) {
          int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
          if (vertexShader == 0) {
              return 0;
          }
          int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
          if (pixelShader == 0) {
              return 0;
          }
  
          int program = GLES20.glCreateProgram();
          checkGlError("glCreateProgram");
          if (program == 0) {
              Log.e(TAG, "Could not create program");
          }
          GLES20.glAttachShader(program, vertexShader);
          checkGlError("glAttachShader");
          GLES20.glAttachShader(program, pixelShader);
          checkGlError("glAttachShader");
          GLES20.glLinkProgram(program);
          int[] linkStatus = new int[1];
          GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
          if (linkStatus[0] != GLES20.GL_TRUE) {
              Log.e(TAG, "Could not link program: ");
              Log.e(TAG, GLES20.glGetProgramInfoLog(program));
              GLES20.glDeleteProgram(program);
              program = 0;
          }
          return program;
      }
  
      /**
       * Compiles the provided shader source.
       *
       * @return A handle to the shader, or 0 on failure.
       */
      public static int loadShader(int shaderType, String source) {
          int shader = GLES20.glCreateShader(shaderType);
          checkGlError("glCreateShader type=" + shaderType);
          GLES20.glShaderSource(shader, source);
          GLES20.glCompileShader(shader);
          int[] compiled = new int[1];
          GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
          if (compiled[0] == 0) {
              Log.e(TAG, "Could not compile shader " + shaderType + ":");
              Log.e(TAG, " " + GLES20.glGetShaderInfoLog(shader));
              GLES20.glDeleteShader(shader);
              shader = 0;
          }
          return shader;
      }
  
      /**
       * Checks to see if a GLES error has been raised.
       */
      public static void checkGlError(String op) {
          int error = GLES20.glGetError();
          if (error != GLES20.GL_NO_ERROR) {
              String msg = op + ": glError 0x" + Integer.toHexString(error);
              Log.e(TAG, msg);
          }
      }
  
      /**
       * Checks to see if the location we obtained is valid.  GLES returns -1 if a label
       * could not be found, but does not set the GL error.
       * <p>
       * Throws a RuntimeException if the location is invalid.
       */
      public static void checkLocation(int location, String label) {
          if (location < 0) {
              Log.e(TAG, "Unable to locate '" + label + "' in program");
          }
      }
  
      /**
       * Creates a texture from raw data.
       *
       * @param data   Image data, in a "direct" ByteBuffer.
       * @param width  Texture width, in pixels (not bytes).
       * @param height Texture height, in pixels.
       * @param format Image data format (use constant appropriate for glTexImage2D(), e.g. GL_RGBA).
       * @return Handle to texture.
       */
      public static int createImageTexture(ByteBuffer data, int width, int height, int format) {
          int[] textureHandles = new int[1];
          int textureHandle;
  
          GLES20.glGenTextures(1, textureHandles, 0);
          textureHandle = textureHandles[0];
          GlUtil.checkGlError("glGenTextures");
  
          // Bind the texture handle to the 2D texture target.
          GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle);
  
          // Configure min/mag filtering, i.e. what scaling method do we use if what we're rendering
          // is smaller or larger than the source image.
          GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
                  GLES20.GL_LINEAR);
          GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
                  GLES20.GL_LINEAR);
          GlUtil.checkGlError("loadImageTexture");
  
          // Load the data from the buffer into the texture handle.
          GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, /*level*/ 0, format,
                  width, height, /*border*/ 0, format, GLES20.GL_UNSIGNED_BYTE, data);
          GlUtil.checkGlError("loadImageTexture");
  
          return textureHandle;
      }
  
      /**
       * Creates a texture from bitmap.
       *
       * @param bmp bitmap data
       * @return Handle to texture.
       */
      public static int createImageTexture(Bitmap bmp) {
          int[] textureHandles = new int[1];
          int textureHandle;
  
          GLES20.glGenTextures(1, textureHandles, 0);
          textureHandle = textureHandles[0];
          GlUtil.checkGlError("glGenTextures");
  
          // Bind the texture handle to the 2D texture target.
          GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle);
  
          // Configure min/mag filtering, i.e. what scaling method do we use if what we're rendering
          // is smaller or larger than the source image.
          GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
                  GLES20.GL_LINEAR);
          GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
                  GLES20.GL_LINEAR);
          GlUtil.checkGlError("loadImageTexture");
  
          // Load the data from the buffer into the texture handle.
          GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, /*level*/ 0, bmp, 0);
          GlUtil.checkGlError("loadImageTexture");
  
          return textureHandle;
      }
  
      /**
       * Allocates a direct float buffer, and populates it with the float array data.
       */
      public static FloatBuffer createFloatBuffer(float[] coords) {
          // Allocate a direct ByteBuffer, using 4 bytes per float, and copy coords into it.
          ByteBuffer bb = ByteBuffer.allocateDirect(coords.length * SIZEOF_FLOAT);
          bb.order(ByteOrder.nativeOrder());
          FloatBuffer fb = bb.asFloatBuffer();
          fb.put(coords);
          fb.position(0);
          return fb;
      }
  
      /**
       * Writes GL version info to the log.
       */
      public static void logVersionInfo() {
          Log.i(TAG, "vendor  : " + GLES20.glGetString(GLES20.GL_VENDOR));
          Log.i(TAG, "renderer: " + GLES20.glGetString(GLES20.GL_RENDERER));
          Log.i(TAG, "version : " + GLES20.glGetString(GLES20.GL_VERSION));
  
          if (false) {
              int[] values = new int[1];
              GLES30.glGetIntegerv(GLES30.GL_MAJOR_VERSION, values, 0);
              int majorVersion = values[0];
              GLES30.glGetIntegerv(GLES30.GL_MINOR_VERSION, values, 0);
              int minorVersion = values[0];
              if (GLES30.glGetError() == GLES30.GL_NO_ERROR) {
                  Log.i(TAG, "iversion: " + majorVersion + "." + minorVersion);
              }
          }
      }
  
  
      /**
       * Creates a texture object suitable for use with this program.
       * <p>
       * On exit, the texture will be bound.
       */
      public static int createTextureObject(int textureTarget) {
          int[] textures = new int[1];
          GLES20.glGenTextures(1, textures, 0);
          GlUtil.checkGlError("glGenTextures");
  
          int texId = textures[0];
          GLES20.glBindTexture(textureTarget, texId);
          GlUtil.checkGlError("glBindTexture " + texId);
  
          GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
                  GLES20.GL_NEAREST);
          GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
                  GLES20.GL_LINEAR);
          GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
                  GLES20.GL_CLAMP_TO_EDGE);
          GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
                  GLES20.GL_CLAMP_TO_EDGE);
          GlUtil.checkGlError("glTexParameter");
  
          return texId;
      }
  
      public static void deleteTextureObject(int textureId) {
          int[] textures = new int[1];
          textures[0] = textureId;
          GLES20.glDeleteTextures(1, textures, 0);
          GlUtil.checkGlError("glDeleteTextures");
      }
  
      public static float[] changeMVPMatrix(float[] mvpMatrix, float viewWidth, float viewHeight, float textureWidth, float textureHeight) {
          float scale = viewWidth * textureHeight / viewHeight / textureWidth;
          if (scale == 1) {
              return mvpMatrix;
          } else {
              float[] mvp = new float[16];
              float[] tmp = new float[16];
              Matrix.setIdentityM(tmp, 0);
              Matrix.scaleM(tmp, 0, scale > 1 ? 1F : (1F / scale), scale > 1 ? scale : 1F, 1F);
              Matrix.multiplyMM(mvp, 0, tmp, 0, mvpMatrix, 0);
              return mvp;
          }
      }
  }
  