diff options
Diffstat (limited to 'glsldebayer.c')
| -rw-r--r-- | glsldebayer.c | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/glsldebayer.c b/glsldebayer.c new file mode 100644 index 0000000..1e0720e --- /dev/null +++ b/glsldebayer.c @@ -0,0 +1,314 @@ +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <EGL/egl.h> +#include <SDL2/SDL.h> +#include <SDL2/SDL_syswm.h> + +GLuint load_gl_shader_from_sources( + GLenum shader_unit, + char const * const * const sources, + GLint const * const lengths ) +{ + GLuint shader = 0; + GLint shader_status = GL_FALSE; + char *shader_infolog = NULL; + do { + size_t n_sources = 0; + while( sources[n_sources] ){ ++n_sources; } + + shader = glCreateShader(shader_unit); + if( !shader ){ break; } + glShaderSource(shader, n_sources, sources, lengths); + glCompileShader(shader); + + glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_status); + if( !shader_status ){ + GLint log_length, returned_length; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); + + shader_infolog = malloc(log_length); + if( shader_infolog ){ + memset(shader_infolog, 0, log_length); + glGetShaderInfoLog( + shader, + log_length, + &returned_length, + shader_infolog ); + char const * shader_unit_str = NULL; + switch(shader_unit) { + case GL_VERTEX_SHADER: shader_unit_str = "vertex"; break; + case GL_FRAGMENT_SHADER: shader_unit_str = "fragment"; break; + } + fprintf(stderr, + "%s shader compilation failed;\n%*s", + shader_unit_str, + returned_length, shader_infolog ); + } + } + } while(0); + + + if( !shader_status ){ glDeleteShader(shader); shader = 0; } + if( shader_infolog ){ free(shader_infolog); } + + return shader; +} + +GLuint load_gl_program_from_sources( + char const * const * const sources_vs, + GLint const * const lengths_vs, + char const * const * const sources_fs, + GLint const * const lengths_fs ) +{ + GLint linkStatus = GL_FALSE; + GLuint program = 0, vert_shader = 0, frag_shader = 0; + char const *msg = NULL; + do { + program = glCreateProgram(); + if( !program ){ fprintf(stderr, "glCreateProgram(): %d", glGetError()); break; } + + if( sources_vs ){ + vert_shader = load_gl_shader_from_sources(GL_VERTEX_SHADER, sources_vs, lengths_vs); + if( !vert_shader ){ break; } + glAttachShader(program, vert_shader); + } + if( sources_fs ){ + frag_shader = load_gl_shader_from_sources(GL_FRAGMENT_SHADER, sources_fs, lengths_fs); + if( !frag_shader ){ break; } + glAttachShader(program, frag_shader); + } + + glLinkProgram(program); + glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); + if( !linkStatus ){ + GLint log_length, returned_length; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length); + + char *program_infolog = malloc(log_length); + if( program_infolog ){ + glGetProgramInfoLog(program, log_length, &returned_length, program_infolog); + fprintf(stderr, + "shader program linking failed;\n%*s", + returned_length, program_infolog ); + free(program_infolog); + } + } + } while(0); + + /* shaders are retained inside the GL context until their last holder, + * i.e. the shader program that was just linked is deleted. */ + if( vert_shader ){ glDeleteShader(vert_shader); } + if( frag_shader ){ glDeleteShader(frag_shader); } + + if( !linkStatus ){ glDeleteProgram(program); program = 0; } + + return program; +} + +extern char const _binary_pixels_raw_start; +extern char const _binary_pixels_raw_end; +#define _binary_pixels_raw ((void const*const)&_binary_pixels_raw_start) +#define _binary_pixels_raw_size (size_t)(&_binary_pixels_raw_end - &_binary_pixels_raw_start) +#define _binary_pixels_raw_width 1280 +#define _binary_pixels_raw_height 960 + +extern char const _binary_debayer_vs_glsl_end; +extern char const _binary_debayer_vs_glsl_start; +#define _binary_debayer_vs_glsl ((void const*const)&_binary_debayer_vs_glsl_start) +#define _binary_debayer_vs_glsl_size (size_t)(&_binary_debayer_vs_glsl_end - &_binary_debayer_vs_glsl_start) + +extern char const _binary_debayer_fs_glsl_end; +extern char const _binary_debayer_fs_glsl_start; +#define _binary_debayer_fs_glsl ((void const*const)&_binary_debayer_fs_glsl_start) +#define _binary_debayer_fs_glsl_size (size_t)(&_binary_debayer_fs_glsl_end - &_binary_debayer_fs_glsl_start) + +static GLenum draw_debayer_RGB(GLuint texture) +{ +#if 1 +/* For the moment, just perform 1st degree unity mapping */ +GLfloat const coef_r[16] = { + 1., 0., 0., 0., + 0., 0., 0., 0., + 0., 0., 0., 0., + 0., 0., 0., 0. }; +GLfloat const *const coef_g = coef_r; +GLfloat const *const coef_b = coef_r; + +/* Those coefficients seem to be way off. Need to investigate further. */ +#elif 0 +GLfloat const coef_r[16] = { + 4.41898209e-01, -1.08617799e-01, -5.05377382e+00, 9.52185120e+00, + 2.05420594e+02, -6.17439055e+02, -2.71301881e+03, 1.26291235e+04, + 4.06578493e+03, -9.34105168e+04, 1.33991823e+05, 1.27240329e+05, + -5.67983982e+05, 6.77352564e+05, -3.70453281e+05, 7.96913561e+04 }; + +GLfloat const coef_g[16] = { + 1.75630153e-01, 1.77970251e-01, 3.93921859e+00, -3.83900750e+01, + -8.50868580e+01, 1.39497351e+03, -1.50332837e+03, -1.85003596e+04, + 6.53562460e+04, -1.00909757e+04, -3.92586418e+05, 1.06909568e+06, + -1.42125035e+06, 1.07029531e+06, -4.38315295e+05, 7.62246706e+04 }; + +GLfloat const coef_b[16] = { + 2.44126153e-01, 1.71659546e-01, 5.35993954e-01, -8.10729044e+00, + -7.11102399e+00, 1.24421866e+02, 1.22255723e-02, -8.74245656e+02, + 4.72294698e+02, 2.99248407e+03, -2.85191269e+03, -4.54081854e+03, + 6.46045466e+03, 1.41007972e+03, -5.12551110e+03, 1.94825904e+03 }; +#else +GLfloat const coef_r[16] = { + 2.43242336e+00, 1.29527225e+01, -1.67053874e+02, -1.03264624e+03, + 3.89533658e+04, -3.79431617e+05, 2.04443576e+06, -7.12156509e+06, + 1.71198465e+07, -2.92874193e+07, 3.60398126e+07, -3.17297224e+07, + 1.95237028e+07, -7.98040387e+06, 1.94790465e+06, -2.14928147e+05 }; + +GLfloat const coef_g[16] = { + 5.24353947e+00, 2.96584994e+01, -1.73865606e+02, -1.93045987e+03, + 1.85513793e+04, -4.48754651e+04, -1.32006487e+05, 1.24471739e+06, + -4.25234811e+06, 8.83297659e+06, -1.23501912e+07, 1.19377860e+07, + -7.90632536e+06, 3.43589308e+06, -8.84463954e+05, 1.02356618e+05 }; + +GLfloat const coef_b[16] = { + 4.04493164e+00, -1.46245348e+01, 1.71095269e+02, 2.06498987e+03, + -4.93803069e+04, 3.95481675e+05, -1.82105431e+06, 5.53531911e+06, + -1.17786850e+07, 1.80202854e+07, -1.99760831e+07, 1.59213215e+07, + -8.89476364e+06, 3.30494584e+06, -7.32930586e+05, 7.33188936e+04 }; +#endif + + static GLuint prog = 0, a_position, u_sampler, u_coef_r, u_coef_g, u_coef_b; + if( !prog ){ + char const *src_vs[] = { _binary_debayer_vs_glsl, NULL }; + GLint const sz_vs[] = { _binary_debayer_vs_glsl_size, 0 }; + char const *src_fs[] = { _binary_debayer_fs_glsl, NULL }; + GLint const sz_fs[] = { _binary_debayer_fs_glsl_size, 0 }; + prog = load_gl_program_from_sources(src_vs, sz_vs, src_fs, sz_fs); + if( !prog ){ _Exit(1); return GL_INVALID_VALUE; } + a_position = glGetAttribLocation(prog, "a_position"); + u_sampler = glGetUniformLocation(prog, "u_sampler"); + u_coef_r = glGetUniformLocation(prog, "u_coef_r"); + u_coef_g = glGetUniformLocation(prog, "u_coef_g"); + u_coef_b = glGetUniformLocation(prog, "u_coef_b"); + } + + static GLuint vbo_xy = 0; + if( !vbo_xy ){ + // a triangle that fully covers the rectangle + // ((0,0),(1,1)) in counter clock vertex order. + GLfloat const data[] = { + 0.0, 0.0, + 2.0, 0.0, + 0.0, 2.0 + }; + glGenBuffers(1, &vbo_xy); + if( !vbo_xy ){ return glGetError(); } + glBindBuffer(GL_ARRAY_BUFFER, vbo_xy); + glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + static GLuint tex; + if( !tex ){ + glGenTextures(1, &tex); + if( !tex ){ return glGetError(); } + glBindTexture(GL_TEXTURE_2D, tex); + if( !(_binary_pixels_raw_width % 4) ){ + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + } else + if( !(_binary_pixels_raw_width % 2) ){ + glPixelStorei(GL_UNPACK_ALIGNMENT, 2); + } else { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + } + /* when loading the pixels into the texture, each two consecutive + * primitive pixels are coalesced into a single 2-channel texel. + * Furthermore, since primitives order are distinct between + * even and odd rows, a texture half the primitive height is created + * but at the primitive picture width. Hence we will end up a + * texture that contains two pictures side by side, even rows + * on the left and odd rows on the right. Since the subpictures + * have uniform primitives arrangement each, we can use built-in + * mipmap generation to downsample the picture for mipmapping + * without impeding the de-Bayering process. */ + glTexImage2D( + /* target = */ GL_TEXTURE_2D, + /* level = */ 0, + /* The 2 component image format of OpenGL-ES 2 + * is GL_LUMINANCE_ALPHA; on later versions you'd + * use GL_RG; internalformat = */ GL_LUMINANCE_ALPHA, + /* width = */ _binary_pixels_raw_width, + /* height = */ _binary_pixels_raw_height/2, + /* border = */ 0, + /* format = */ GL_LUMINANCE_ALPHA, + /* type = */ GL_UNSIGNED_BYTE, + /* data = */ _binary_pixels_raw ); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + } + + glUseProgram(prog); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, tex); + glUniform1i(u_sampler, 0); + glUniformMatrix4fv(u_coef_r, 1, GL_FALSE, coef_r); + glUniformMatrix4fv(u_coef_g, 1, GL_FALSE, coef_g); + glUniformMatrix4fv(u_coef_b, 1, GL_FALSE, coef_b); + glBindBuffer(GL_ARRAY_BUFFER, vbo_xy); + glEnableVertexAttribArray(a_position); + glVertexAttribPointer(a_position, 2, GL_FLOAT, GL_FALSE, 0, NULL); + glDrawArrays(GL_TRIANGLES, 0, 3); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + glUseProgram(0); + + return glGetError(); +} + +int main() +{ + int screenwidth = _binary_pixels_raw_width; + int screenheight = _binary_pixels_raw_height; + + EGLint numConfigs, majorVersion, minorVersion; + SDL_Window *window = SDL_CreateWindow( + "GLESv2 / fragment shader de-Bayer", 0, 0, + screenwidth, screenheight, SDL_WINDOW_OPENGL); + + EGLConfig config = {0}; + EGLint const egl_config_attr[] = { + EGL_BUFFER_SIZE, 24, + EGL_DEPTH_SIZE, 24, + EGL_STENCIL_SIZE, 8, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(display, &majorVersion, &minorVersion); + eglChooseConfig(display, egl_config_attr, &config, 1, &numConfigs); + + SDL_SysWMinfo sysInfo; + SDL_VERSION(&sysInfo.version); + SDL_GetWindowWMInfo(window, &sysInfo); + + EGLint const egl_context_attr[] = { + EGL_CONTEXT_MAJOR_VERSION, 2, + EGL_NONE + }; + EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, egl_context_attr); + EGLSurface surface = eglCreateWindowSurface(display, config, (EGLNativeWindowType)sysInfo.info.x11.window, 0); // X11? + eglMakeCurrent(display, surface, surface, context); + eglSwapInterval(display, 1); + + for( SDL_Event event = {0} + ; SDL_QUIT != event.type + ; SDL_PollEvent(&event) + ){ + glViewport(0, 0, screenwidth, screenheight); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + draw_debayer_RGB(0); + eglSwapBuffers(display, surface); + } + + eglDestroySurface(display, surface); + eglDestroyContext(display, context); + eglTerminate(display); + SDL_DestroyWindow(window); + return 0; +} |
