/*
* Copyright 2013 Hannes Janetzek
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.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 <http://www.gnu.org/licenses/>.
*/
package org.oscim.renderer;
import static org.oscim.backend.GLAdapter.gl;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import org.oscim.backend.GL;
import org.oscim.backend.GLAdapter;
import org.oscim.utils.FastMath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility functions
*/
public class GLUtils {
static final Logger log = LoggerFactory.getLogger(GLUtils.class);
public static void setColor(int location, int color, float alpha) {
if (alpha >= 1)
alpha = ((color >>> 24) & 0xff) / 255f;
else if (alpha < 0)
alpha = 0;
else
alpha *= ((color >>> 24) & 0xff) / 255f;
if (alpha == 1) {
gl.uniform4f(location,
((color >>> 16) & 0xff) / 255f,
((color >>> 8) & 0xff) / 255f,
((color >>> 0) & 0xff) / 255f,
alpha);
} else {
gl.uniform4f(location,
((color >>> 16) & 0xff) / 255f * alpha,
((color >>> 8) & 0xff) / 255f * alpha,
((color >>> 0) & 0xff) / 255f * alpha,
alpha);
}
}
public static void setColorBlend(int location, int color1, int color2, float mix) {
float a1 = (((color1 >>> 24) & 0xff) / 255f) * (1 - mix);
float a2 = (((color2 >>> 24) & 0xff) / 255f) * mix;
gl.uniform4f(location,
((((color1 >>> 16) & 0xff) / 255f) * a1
+ (((color2 >>> 16) & 0xff) / 255f) * a2),
((((color1 >>> 8) & 0xff) / 255f) * a1
+ (((color2 >>> 8) & 0xff) / 255f) * a2),
((((color1 >>> 0) & 0xff) / 255f) * a1
+ (((color2 >>> 0) & 0xff) / 255f) * a2),
(a1 + a2));
}
public static void setTextureParameter(int min_filter, int mag_filter, int wrap_s, int wrap_t) {
gl.texParameterf(GL.TEXTURE_2D,
GL.TEXTURE_MIN_FILTER,
min_filter);
gl.texParameterf(GL.TEXTURE_2D,
GL.TEXTURE_MAG_FILTER,
mag_filter);
gl.texParameterf(GL.TEXTURE_2D,
GL.TEXTURE_WRAP_S,
wrap_s); // Set U Wrapping
gl.texParameterf(GL.TEXTURE_2D,
GL.TEXTURE_WRAP_T,
wrap_t); // Set V Wrapping
}
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);
GLState.bindTex2D(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);
IntBuffer intBuf = buf.asIntBuffer();
gl.texImage2D(GL.TEXTURE_2D, 0, format, width, height, 0, format,
GL.UNSIGNED_BYTE, intBuf);
GLState.bindTex2D(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, GL.ALPHA,
GL.LINEAR, GL.LINEAR,
// GLES20.GL_NEAREST, GLES20.GL_NEAREST,
GL.REPEAT,
GL.REPEAT);
}
public static void checkGlError(String op) {
//GL = GLAdapter.get();
int error;
while ((error = gl.getError()) != 0) { // GL20.NO_ERROR) {
log.error(op + ": glError " + error);
// throw new RuntimeException(op + ": glError " + error);
}
}
public static boolean checkGlOutOfMemory(String op) {
int error;
boolean oom = false;
while ((error = gl.getError()) != 0) {// GL20.NO_ERROR) {
log.error(op + ": glError " + error);
// throw new RuntimeException(op + ": glError " + error);
if (error == 1285)
oom = true;
}
return oom;
}
public static void setColor(int handle, float[] c, float alpha) {
if (alpha >= 1) {
gl.uniform4f(handle, c[0], c[1], c[2], c[3]);
} else {
if (alpha < 0) {
log.debug("setColor: " + alpha);
alpha = 0;
gl.uniform4f(handle, 0, 0, 0, 0);
}
gl.uniform4f(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 = MapRenderer.getFloatBuffer(count * 4);
buf.put(val);
buf.flip();
gl.uniform4fv(location, count, buf);
}
public static int[] glGenBuffers(int num) {
IntBuffer buf = MapRenderer.getIntBuffer(num);
buf.position(0);
buf.limit(num);
gl.genBuffers(num, buf);
int[] ret = new int[num];
buf.position(0);
buf.limit(num);
buf.get(ret);
return ret;
}
public static void glDeleteBuffers(int num, int[] ids) {
IntBuffer buf = MapRenderer.getIntBuffer(num);
buf.put(ids, 0, num);
buf.flip();
gl.deleteBuffers(num, buf);
}
public static int[] glGenTextures(int num) {
if (num <= 0)
return null;
int[] ret = new int[num];
IntBuffer buf = MapRenderer.getIntBuffer(num);
if (GLAdapter.GDX_WEBGL_QUIRKS) {
for (int i = 0; i < num; i++) {
gl.genTextures(num, buf);
buf.position(0);
ret[i] = buf.get();
buf.position(0);
}
} else {
gl.genTextures(num, buf);
buf.position(0);
buf.get(ret);
}
return ret;
}
public static void glDeleteTextures(int num, int[] ids) {
IntBuffer buf = MapRenderer.getIntBuffer(num);
buf.put(ids, 0, num);
buf.flip();
gl.deleteTextures(num, buf);
}
}