/* * Copyright 2010, 2011, 2012 mapsforge.org * * This program is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package org.oscim.utils; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.IntBuffer; import org.oscim.backend.GL20; import org.oscim.backend.GLAdapter; import org.oscim.backend.Log; import org.oscim.renderer.GLRenderer; /** * Utility functions */ public class GlUtils { private static final GL20 GL = GLAdapter.INSTANCE; public static native void setColor(int location, int color, float alpha); public static native void setColorBlend(int location, int color1, int color2, float mix); private static String TAG = "GlUtils"; public static void setTextureParameter(int min_filter, int mag_filter, int wrap_s, int wrap_t) { GL.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MIN_FILTER, min_filter); GL.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER, mag_filter); GL.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_S, wrap_s); // Set U Wrapping GL.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_T, wrap_t); // Set V Wrapping } // /** // * @param bitmap // * ... // * @return textureId // */ // public static int loadTextures(Bitmap bitmap) { // // int[] textures = new int[1]; // GLES20.glGenTextures(1, textures, 0); // // int textureID = textures[0]; // // GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureID); // // setTextureParameter(GLES20.GL_LINEAR, GLES20.GL_LINEAR, // GLES20.GL_CLAMP_TO_EDGE, GLES20.GL_CLAMP_TO_EDGE); // // GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); // // return textureID; // } public static int loadTexture(byte[] pixel, int width, int height, int format, int min_filter, int mag_filter, int wrap_s, int wrap_t) { int[] textureIds = GlUtils.glGenTextures(1); GL.glBindTexture(GL20.GL_TEXTURE_2D, textureIds[0]); setTextureParameter(min_filter, mag_filter, wrap_s, wrap_t); ByteBuffer buf = ByteBuffer.allocateDirect(width * height).order(ByteOrder.nativeOrder()); buf.put(pixel); buf.position(0); GL.glTexImage2D(GL20.GL_TEXTURE_2D, 0, format, width, height, 0, format, GL20.GL_UNSIGNED_BYTE, buf); GL.glBindTexture(GL20.GL_TEXTURE_2D, 0); return textureIds[0]; } public static int loadStippleTexture(byte[] stipple) { int sum = 0; for (byte flip : stipple) sum += flip; byte[] pixel = new byte[sum]; boolean on = true; int pos = 0; for (byte flip : stipple) { float max = flip; for (int s = 0; s < flip; s++) { float color = Math.abs(s / (max - 1) - 0.5f); if (on) color = 255 * (1 - color); else color = 255 * color; pixel[pos + s] = FastMath.clampToByte((int) color); } on = !on; pos += flip; } return loadTexture(pixel, sum, 1, GL20.GL_ALPHA, GL20.GL_LINEAR, GL20.GL_LINEAR, //GLES20.GL_NEAREST, GLES20.GL_NEAREST, GL20.GL_REPEAT, GL20.GL_REPEAT); } /** * @param shaderType * shader type * @param source * shader code * @return gl identifier */ public static int loadShader(int shaderType, String source) { int shader = GL.glCreateShader(shaderType); if (shader != 0) { GL.glShaderSource(shader, source); GL.glCompileShader(shader); IntBuffer compiled = GLRenderer.getIntBuffer(1); GL.glGetShaderiv(shader, GL20.GL_COMPILE_STATUS, compiled); if (compiled.get() == 0) { Log.e(TAG, "Could not compile shader " + shaderType + ":"); Log.e(TAG, GL.glGetShaderInfoLog(shader)); GL.glDeleteShader(shader); shader = 0; } } return shader; } /** * @param vertexSource * ... * @param fragmentSource * ... * @return gl identifier */ public static int createProgram(String vertexSource, String fragmentSource) { int vertexShader = loadShader(GL20.GL_VERTEX_SHADER, vertexSource); if (vertexShader == 0) { return 0; } int pixelShader = loadShader(GL20.GL_FRAGMENT_SHADER, fragmentSource); if (pixelShader == 0) { return 0; } int program = GL.glCreateProgram(); if (program != 0) { checkGlError("glCreateProgram"); GL.glAttachShader(program, vertexShader); checkGlError("glAttachShader"); GL.glAttachShader(program, pixelShader); checkGlError("glAttachShader"); GL.glLinkProgram(program); IntBuffer linkStatus = GLRenderer.getIntBuffer(1); GL.glGetProgramiv(program, GL20.GL_LINK_STATUS, linkStatus); if (linkStatus.get() != GL20.GL_TRUE) { Log.e(TAG, "Could not link program: "); Log.e(TAG, GL.glGetProgramInfoLog(program)); GL.glDeleteProgram(program); program = 0; } } return program; } /** * @param op * ... */ public static void checkGlError(String op) { int error; while ((error = GL.glGetError()) != GL20.GL_NO_ERROR) { Log.e(TAG, op + ": glError " + error); // throw new RuntimeException(op + ": glError " + error); } } public static boolean checkGlOutOfMemory(String op) { int error; boolean oom = false; while ((error = GL.glGetError()) != GL20.GL_NO_ERROR) { Log.e(TAG, op + ": glError " + error); // throw new RuntimeException(op + ": glError " + error); if (error == 1285) oom = true; } return oom; } // public static void setBlendColors(int handle, float[] c1, float[] c2, float mix) { // if (mix <= 0f) // GLES20.glUniform4fv(handle, 1, c1, 0); // else if (mix >= 1f) // GLES20.glUniform4fv(handle, 1, c2, 0); // else { // GLES20.glUniform4f(handle, // c1[0] * (1 - mix) + c2[0] * mix, // c1[1] * (1 - mix) + c2[1] * mix, // c1[2] * (1 - mix) + c2[2] * mix, // c1[3] * (1 - mix) + c2[3] * mix); // } // } public static void setColor(int handle, float[] c, float alpha) { if (alpha >= 1) { GL.glUniform4f(handle, c[0], c[1], c[2], c[3]); } else { if (alpha < 0) { Log.d(TAG, "setColor: " + alpha); alpha = 0; GL.glUniform4f(handle, 0, 0, 0, 0); } GL.glUniform4f(handle, c[0] * alpha, c[1] * alpha, c[2] * alpha, c[3] * alpha); } } public static float[] colorToFloat(int color) { float[] c = new float[4]; c[3] = (color >> 24 & 0xff) / 255.0f; c[0] = (color >> 16 & 0xff) / 255.0f; c[1] = (color >> 8 & 0xff) / 255.0f; c[2] = (color >> 0 & 0xff) / 255.0f; return c; } // premultiply alpha public static float[] colorToFloatP(int color) { float[] c = new float[4]; c[3] = (color >> 24 & 0xff) / 255.0f; c[0] = (color >> 16 & 0xff) / 255.0f * c[3]; c[1] = (color >> 8 & 0xff) / 255.0f * c[3]; c[2] = (color >> 0 & 0xff) / 255.0f * c[3]; return c; } /** * public-domain function by Darel Rex Finley * from http://alienryderflex.com/saturation.html * * @param color * The passed-in RGB values can be on any desired scale, such as * 0 to 1, or 0 to 255. * @param change * 0.0 creates a black-and-white image. * 0.5 reduces the color saturation by half. * 1.0 causes no change. * 2.0 doubles the color saturation. */ public static void changeSaturation(float color[], float change) { float r = color[0]; float g = color[1]; float b = color[2]; double p = Math.sqrt(r * r * 0.299f + g * g * 0.587f + b * b * 0.114f); color[0] = FastMath.clampN((float) (p + (r - p) * change)); color[1] = FastMath.clampN((float) (p + (g - p) * change)); color[2] = FastMath.clampN((float) (p + (b - p) * change)); } public static void glUniform4fv(int location, int count, float[] val) { FloatBuffer buf = GLRenderer.getFloatBuffer(count * 4); buf.put(val); buf.flip(); GL.glUniform4fv(location, count, buf); } public static int[] glGenBuffers(int num) { IntBuffer buf = GLRenderer.getIntBuffer(num); GL.glGenBuffers(num, buf); int[] ret = new int[num]; buf.get(ret); return ret; } public static void glDeleteBuffers(int num, int[] ids) { IntBuffer buf = GLRenderer.getIntBuffer(num); buf.put(ids, 0, num); GL.glDeleteBuffers(num, buf); } public static int[] glGenTextures(int num) { IntBuffer buf = GLRenderer.getIntBuffer(num); GL.glGenTextures(num, buf); int[] ret = new int[num]; buf.get(ret); return ret; } public static void glDeleteTextures(int num, int[] ids) { IntBuffer buf = GLRenderer.getIntBuffer(num); buf.put(ids, 0, num); GL.glDeleteTextures(num, buf); } // private final static float[] mIdentity = { // 1, 0, 0, 0, // 0, 1, 0, 0, // 0, 0, 1, 0, // 0, 0, 0, 1 }; // // public static void setTileMatrix(float[] matrix, float tx, float ty, float s) { // System.arraycopy(mIdentity, 0, matrix, 0, 16); // // scale tile relative to map scale // matrix[0] = matrix[5] = s / GLRenderer.COORD_SCALE; // // translate relative to map center // matrix[12] = tx * s; // matrix[13] = ty * s; // } // // public static void setTranslation(float[] matrix, float x, float y, float z) { // System.arraycopy(mIdentity, 0, matrix, 0, 16); // matrix[12] = x; // matrix[13] = y; // matrix[14] = z; // } // // public static void setMatrix(float[] matrix, float tx, float ty, float scale) { // System.arraycopy(mIdentity, 0, matrix, 0, 16); // matrix[12] = tx; // matrix[13] = ty; // matrix[0] = scale; // matrix[5] = scale; // //matrix[10] = scale; // } // // public static void setIdentity(float[] matrix) { // System.arraycopy(mIdentity, 0, matrix, 0, 16); // } // // public static void setScaleM(float[] matrix, float sx, float sy, float sz) { // System.arraycopy(mIdentity, 0, matrix, 0, 16); // matrix[0] = sx; // matrix[5] = sy; // matrix[10] = sz; // } // // public static void addOffsetM(float[] matrix, int delta) { // // from http://www.mathfor3dgameprogramming.com/code/Listing9.1.cpp // // float n = MapViewPosition.VIEW_NEAR; // // float f = MapViewPosition.VIEW_FAR; // // float pz = 1; // // float epsilon = -2.0f * f * n * delta / ((f + n) * pz * (pz + delta)); // float epsilon = 1.0f / (1 << 11); // // matrix[10] *= 1.0f + epsilon * delta; // } }