gr-fosphor/lib/fosphor/gl_cmap.c

325 lines
7.0 KiB
C

/*
* gl_cmap.c
*
* OpenGL float texture -> color mapping
*
* Copyright (C) 2013-2021 Sylvain Munaut
* SPDX-License-Identifier: GPL-3.0-or-later
*/
/*! \addtogroup gl/cmap
* @{
*/
/*! \file gl_cmap.c
* \brief OpenGL float texture to color mapping
*/
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "gl_platform.h"
#include "gl_cmap.h"
#include "resource.h"
struct gl_cmap_shader
{
/* Status */
int loaded;
/* Shader handles */
GLuint prog;
GLuint shader;
/* Uniforms */
GLint u_tex;
GLint u_palette;
GLint u_range;
};
enum gl_cmap_shader_type
{
GL_CMAP_SHADER_SIMPLE,
GL_CMAP_SHADER_BICUBIC,
GL_CMAP_SHADER_FALLBACK,
_GL_CMAP_SHADER_NUM
};
struct fosphor_gl_cmap_ctx {
struct gl_cmap_shader shaders[_GL_CMAP_SHADER_NUM];
};
/* -------------------------------------------------------------------------- */
/* Helpers / Internal API */
/* -------------------------------------------------------------------------- */
static int
gl_cmap_init_shader(struct gl_cmap_shader *shader, const char *name)
{
const char *shader_src;
GLint buf_len, orv;
/* Load shader sources */
shader_src = resource_get(name, NULL);
if (!shader_src)
return -ENOENT;
/* Allocate shader */
shader->prog = glCreateProgram();
shader->shader = glCreateShader(GL_FRAGMENT_SHADER);
/* Compile / Link / Attach */
glShaderSource(shader->shader, 1, (const char **)&shader_src, NULL);
glCompileShader(shader->shader);
/* Check success and compile log */
glGetShaderiv(shader->shader, GL_COMPILE_STATUS, &orv);
glGetShaderiv(shader->shader, GL_INFO_LOG_LENGTH, &buf_len);
#if 1
if ((buf_len > 0) && (orv != GL_TRUE))
#else
if (buf_len > 0)
#endif
{
char *buf = malloc(buf_len+1);
glGetShaderInfoLog(shader->shader, buf_len, 0, buf);
buf[buf_len] = '\0';
fprintf(stderr, "[!] gl_cmap shader compile log :\n%s\n", buf);
free(buf);
}
if (orv != GL_TRUE) {
fprintf(stderr, "[!] gl_cmap shader compilation failed (%s)\n", name);
return -EINVAL;
}
/* Attach to program */
glAttachShader(shader->prog, shader->shader);
glLinkProgram(shader->prog);
/* Grab the uniform locations */
shader->u_tex = glGetUniformLocation(shader->prog, "tex");
shader->u_palette = glGetUniformLocation(shader->prog, "palette");
shader->u_range = glGetUniformLocation(shader->prog, "range");
/* Success */
shader->loaded = 1;
return 0;
}
static void
gl_cmap_release_shader(struct gl_cmap_shader *shader)
{
if (!shader->loaded)
return;
glDetachShader(shader->prog, shader->shader);
glDeleteShader(shader->shader);
glDeleteProgram(shader->prog);
memset(shader, 0x00, sizeof(struct gl_cmap_shader));
}
/* -------------------------------------------------------------------------- */
/* Exposed API */
/* -------------------------------------------------------------------------- */
struct fosphor_gl_cmap_ctx *
fosphor_gl_cmap_init(void)
{
struct fosphor_gl_cmap_ctx *cmap_ctx;
int rv;
int need_fallback = 0;
/* Allocate structure */
cmap_ctx = malloc(sizeof(struct fosphor_gl_cmap_ctx));
if (!cmap_ctx)
return NULL;
memset(cmap_ctx, 0, sizeof(struct fosphor_gl_cmap_ctx));
/* Init shaders */
rv = gl_cmap_init_shader(
&cmap_ctx->shaders[GL_CMAP_SHADER_SIMPLE],
"cmap_simple.glsl"
);
if (rv) {
fprintf(stderr, "[w] Color map shader 'simple' failed to load, will use fallback\n");
need_fallback = 1;
}
rv = gl_cmap_init_shader(
&cmap_ctx->shaders[GL_CMAP_SHADER_BICUBIC],
"cmap_bicubic.glsl"
);
if (rv) {
fprintf(stderr, "[w] Color map shader 'bicubic' failed to load, will use fallback\n");
need_fallback = 1;
}
if (need_fallback) {
rv = gl_cmap_init_shader(
&cmap_ctx->shaders[GL_CMAP_SHADER_FALLBACK],
"cmap_fallback.glsl"
);
if (rv) {
fprintf(stderr, "[!] Color map shader 'fallback' failed, aborting\n");
goto error;
}
}
/* All done */
return cmap_ctx;
/* Error path */
error:
fosphor_gl_cmap_release(cmap_ctx);
return NULL;
}
void
fosphor_gl_cmap_release(struct fosphor_gl_cmap_ctx *cmap_ctx)
{
/* Safety */
if (!cmap_ctx)
return;
/* Disable program */
glUseProgram(0);
/* Release shaders */
gl_cmap_release_shader(&cmap_ctx->shaders[GL_CMAP_SHADER_SIMPLE]);
gl_cmap_release_shader(&cmap_ctx->shaders[GL_CMAP_SHADER_BICUBIC]);
gl_cmap_release_shader(&cmap_ctx->shaders[GL_CMAP_SHADER_FALLBACK]);
/* Release container */
free(cmap_ctx);
}
void
fosphor_gl_cmap_enable(struct fosphor_gl_cmap_ctx *cmap_ctx,
GLuint tex_id, GLuint cmap_id,
float scale, float offset,
enum fosphor_gl_cmap_mode mode)
{
struct gl_cmap_shader *shader;
float range[2];
int fmode;
shader = &cmap_ctx->shaders[
mode == GL_CMAP_MODE_BICUBIC ?
GL_CMAP_SHADER_BICUBIC :
GL_CMAP_SHADER_SIMPLE
];
if (!shader->loaded)
shader = &cmap_ctx->shaders[GL_CMAP_SHADER_FALLBACK];
/* Enable program */
glUseProgram(shader->prog);
/* Texture unit 0: Main texture */
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_id);
glUniform1i(shader->u_tex, 0);
fmode = (mode == GL_CMAP_MODE_NEAREST) ? GL_NEAREST : GL_LINEAR;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, fmode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, fmode);
/* Texture unit 1: Palette 1D texture */
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_1D, cmap_id);
glUniform1i(shader->u_palette, 1);
/* Range definition */
range[0] = scale;
range[1] = offset;
glUniform2fv(shader->u_range, 1, range);
}
void
fosphor_gl_cmap_disable(void)
{
/* Default to texture unit 0 */
glActiveTexture(GL_TEXTURE0);
/* Disable program */
glUseProgram(0);
}
void
fosphor_gl_cmap_draw_scale(GLuint cmap_id,
float x0, float x1, float y0, float y1)
{
/* Enable texture-1D */
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_1D, cmap_id);
glEnable(GL_TEXTURE_1D);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
/* Draw QUAD */
glBegin( GL_QUADS );
glTexCoord1f(0.0f); glVertex2f(x0, y0);
glTexCoord1f(0.0f); glVertex2f(x1, y0);
glTexCoord1f(1.0f); glVertex2f(x1, y1);
glTexCoord1f(1.0f); glVertex2f(x0, y1);
glEnd();
/* Disable texturing */
glDisable(GL_TEXTURE_1D);
}
int
fosphor_gl_cmap_generate(GLuint *cmap_id, gl_cmap_gen_func_t gfn, void *gfn_arg, int N)
{
uint32_t *rgba;
/* Temp array for data */
rgba = malloc(sizeof(uint32_t) * N);
if (!rgba)
return -1;
/* Allocate texture ID */
glGenTextures(1, cmap_id);
/* Select it */
glBindTexture(GL_TEXTURE_1D, *cmap_id);
/* Generate texture data */
gfn(rgba, N, gfn_arg);
/* Upload data */
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, N, 0,
GL_RGBA, GL_UNSIGNED_BYTE, rgba);
/* Configure texture behavior */
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
/* Release temp array */
free(rgba);
return 0;
}
/*! @} */