From 91817b167a8f9b33ff82fc2ee46c276968411f0d Mon Sep 17 00:00:00 2001 From: Benjamin Schaaf Date: Sun, 18 Apr 2021 23:13:56 +1000 Subject: [PATCH] Reorganization, replacing CPU debayering entirely --- camera.glade | 592 ------------------ data/blit.frag | 2 + data/blit.vert | 2 + camera.css => data/camera.css | 0 camera.ui => data/camera.ui | 1 + data/debayer.frag | 37 +- data/debayer.vert | 15 +- .../folder-symbolic.svg | 0 data/meson.build | 15 + .../org.postmarketos.Megapixels.gresource.xml | 15 + postprocess.sh => data/postprocess.sh | 0 .../settings-symbolic.svg | 0 shutter-button.svg => data/shutter-button.svg | 0 switch-camera.svg => data/switch-camera.svg | 0 gl_quickpreview.c | 207 ------ gl_quickpreview.h | 21 - gl_utils.c | 128 ---- gl_utils.h | 13 - meson.build | 90 ++- org.postmarketos.Megapixels.gresource.xml | 11 - quickpreview.c | 351 ----------- quickpreview.h | 14 - camera.c => src/camera.c | 0 camera.h => src/camera.h | 0 camera_config.c => src/camera_config.c | 0 camera_config.h => src/camera_config.h | 0 device.c => src/device.c | 0 device.h => src/device.h | 0 src/gl_util.c | 220 +++++++ src/gl_util.h | 17 + src/gles2_debayer.c | 141 +++++ src/gles2_debayer.h | 21 + ini.c => src/ini.c | 0 ini.h => src/ini.h | 0 io_pipeline.c => src/io_pipeline.c | 0 io_pipeline.h => src/io_pipeline.h | 0 main.c => src/main.c | 70 +-- main.h => src/main.h | 0 matrix.c => src/matrix.c | 0 matrix.h => src/matrix.h | 0 pipeline.c => src/pipeline.c | 0 pipeline.h => src/pipeline.h | 0 process_pipeline.c => src/process_pipeline.c | 125 ++-- process_pipeline.h => src/process_pipeline.h | 0 {renderdoc => src/renderdoc}/app.h | 0 zbar_pipeline.c => src/zbar_pipeline.c | 0 zbar_pipeline.h => src/zbar_pipeline.h | 0 tests/test_quickpreview.c | 120 ---- 48 files changed, 571 insertions(+), 1657 deletions(-) delete mode 100644 camera.glade rename camera.css => data/camera.css (100%) rename camera.ui => data/camera.ui (99%) rename folder-symbolic.svg => data/folder-symbolic.svg (100%) create mode 100644 data/meson.build create mode 100644 data/org.postmarketos.Megapixels.gresource.xml rename postprocess.sh => data/postprocess.sh (100%) rename settings-symbolic.svg => data/settings-symbolic.svg (100%) rename shutter-button.svg => data/shutter-button.svg (100%) rename switch-camera.svg => data/switch-camera.svg (100%) delete mode 100644 gl_quickpreview.c delete mode 100644 gl_quickpreview.h delete mode 100644 gl_utils.c delete mode 100644 gl_utils.h delete mode 100644 org.postmarketos.Megapixels.gresource.xml delete mode 100644 quickpreview.c delete mode 100644 quickpreview.h rename camera.c => src/camera.c (100%) rename camera.h => src/camera.h (100%) rename camera_config.c => src/camera_config.c (100%) rename camera_config.h => src/camera_config.h (100%) rename device.c => src/device.c (100%) rename device.h => src/device.h (100%) create mode 100644 src/gl_util.c create mode 100644 src/gl_util.h create mode 100644 src/gles2_debayer.c create mode 100644 src/gles2_debayer.h rename ini.c => src/ini.c (100%) rename ini.h => src/ini.h (100%) rename io_pipeline.c => src/io_pipeline.c (100%) rename io_pipeline.h => src/io_pipeline.h (100%) rename main.c => src/main.c (95%) rename main.h => src/main.h (100%) rename matrix.c => src/matrix.c (100%) rename matrix.h => src/matrix.h (100%) rename pipeline.c => src/pipeline.c (100%) rename pipeline.h => src/pipeline.h (100%) rename process_pipeline.c => src/process_pipeline.c (85%) rename process_pipeline.h => src/process_pipeline.h (100%) rename {renderdoc => src/renderdoc}/app.h (100%) rename zbar_pipeline.c => src/zbar_pipeline.c (100%) rename zbar_pipeline.h => src/zbar_pipeline.h (100%) delete mode 100644 tests/test_quickpreview.c diff --git a/camera.glade b/camera.glade deleted file mode 100644 index 89d7872..0000000 --- a/camera.glade +++ /dev/null @@ -1,592 +0,0 @@ - - - - - - 100 - 1 - 10 - - - False - Camera - 360 - 640 - - - True - False - - - True - False - vertical - - - True - False - vertical - - - True - False - - - True - True - 0 - - - - - - True - True - 0 - - - - - False - - - True - False - 10 - 10 - 10 - 10 - - - True - False - 10 - ISO - - - False - True - 0 - - - - - True - True - control_adj - 1 - False - - - True - True - 1 - - - - - Auto - True - True - True - 10 - - - False - True - end - 2 - - - - - True - True - 0 - - - - - - False - True - 1 - - - - - True - False - - - 48 - 48 - True - True - True - True - - - True - False - /org/postmarketos/Megapixels/shutter-button.svg - - - - - - False - True - 2 - - - - - True - False - 8 - 8 - 10 - - - True - True - True - - - True - False - /org/postmarketos/Megapixels/settings-symbolic.svg - - - - - False - True - 0 - - - - - True - True - True - - - True - False - /org/postmarketos/Megapixels/switch-camera.svg - - - - - False - True - 1 - - - - - - - - True - True - 10 - 0 - - - - - True - False - 8 - 8 - 10 - - - - - - True - True - True - - - True - False - /org/postmarketos/Megapixels/folder-symbolic.svg - - - - - False - True - end - 1 - - - - - True - True - True - - - True - False - - - 24 - 24 - True - False - - - - - 24 - 24 - True - False - - - - - - - False - True - end - 2 - - - - - True - True - 10 - end - 1 - - - - - False - True - 10 - end - 1 - - - - - False - - - True - False - 10 - 10 - 10 - 10 - 10 - - - True - False - start - No error - - - True - True - 0 - - - - - gtk-close - True - True - True - True - True - - - False - True - 1 - - - - - True - True - 0 - - - - - - False - True - 2 - - - - - main - page0 - - - - - True - True - in - - - True - False - none - - - True - False - 10 - 10 - 10 - 10 - vertical - 10 - - - True - False - - - Back - True - True - True - 10 - 10 - - - - False - True - 0 - - - - - True - False - Settings aren't functional yet - - - False - True - 1 - - - - - False - True - 0 - - - - - True - False - vertical - 4 - - - True - False - start - Photos - - - - False - True - 0 - - - - - True - False - 0 - in - - - True - False - 12 - - - True - False - vertical - 6 - - - True - False - start - Resolution - - - False - True - 0 - - - - - True - False - - - False - True - 1 - - - - - - - - - - - True - False - start - Storage mode - - - False - True - 4 - - - - - True - False - vertical - - - Debayer with VNG (slowest) - True - True - False - True - True - - - False - True - 0 - - - - - Debayer with linear interpolation - True - True - False - True - store_vng - - - False - True - 1 - - - - - Raw - True - True - False - True - store_vng - - - False - True - 2 - - - - - False - True - 5 - - - - - - - - - - - - - False - True - 1 - - - - - False - True - 1 - - - - - - - - - - - - settings - page1 - 1 - - - - - - diff --git a/data/blit.frag b/data/blit.frag index 43ea399..1f1b314 100644 --- a/data/blit.frag +++ b/data/blit.frag @@ -1,4 +1,6 @@ +#ifdef GL_ES precision mediump float; +#endif uniform sampler2D texture; diff --git a/data/blit.vert b/data/blit.vert index b4576b8..557c872 100644 --- a/data/blit.vert +++ b/data/blit.vert @@ -1,4 +1,6 @@ +#ifdef GL_ES precision mediump float; +#endif attribute vec2 vert; attribute vec2 tex_coord; diff --git a/camera.css b/data/camera.css similarity index 100% rename from camera.css rename to data/camera.css diff --git a/camera.ui b/data/camera.ui similarity index 99% rename from camera.ui rename to data/camera.ui index bb06696..9b137f0 100644 --- a/camera.ui +++ b/data/camera.ui @@ -25,6 +25,7 @@ 1 0 + 1 diff --git a/data/debayer.frag b/data/debayer.frag index 94ecc50..8e9f184 100644 --- a/data/debayer.frag +++ b/data/debayer.frag @@ -1,37 +1,28 @@ -precision highp float; +#ifdef GL_ES +precision mediump float; +#endif uniform sampler2D texture; -uniform sampler2D pixel_layout; uniform mat3 color_matrix; -uniform vec2 pixel_size; - -// varying vec2 uv1; -// varying vec2 uv2; varying vec2 top_left_uv; varying vec2 top_right_uv; varying vec2 bottom_left_uv; varying vec2 bottom_right_uv; -varying vec2 half_pixel_coord; - -#define fetch(p) texture2D(texture, p).r void main() { - vec4 pixels = vec4( - fetch(top_left_uv), - fetch(top_right_uv), - fetch(bottom_right_uv), - fetch(bottom_left_uv)); + // Note the coordinates for texture samples need to be a varying, as the + // Mali-400 has this as a fast path allowing 32-bit floats. Otherwise + // they end up as 16-bit floats and that's not accurate enough. + vec4 samples = vec4( + texture2D(texture, top_left_uv).r, + texture2D(texture, top_right_uv).r, + texture2D(texture, bottom_left_uv).r, + texture2D(texture, bottom_right_uv).r); - vec2 arrangement = floor(half_pixel_coord); - - vec2 is_bottom_left = step(1.0, arrangement); - - // vec3 color = mix( - // mix(pixels.xyz, pixels.yzw, is_bottom_left.y), - // mix(pixels.wzy, pixels.zyx, is_bottom_left.y), - // is_bottom_left.x); - vec3 color = pixels.zyx; + // Assume BGGR for now. Currently this just takes 3 of the four samples + // for each pixel, there's room here to do some better debayering. + vec3 color = samples.wyx; // Fast SRGB estimate. See https://mimosa-pudica.net/fast-gamma/ vec3 srgb_color = (vec3(1.138) * inversesqrt(color) - vec3(0.138)) * color; diff --git a/data/debayer.vert b/data/debayer.vert index d06d511..49af8c1 100644 --- a/data/debayer.vert +++ b/data/debayer.vert @@ -1,20 +1,17 @@ -precision highp float; +#ifdef GL_ES +precision mediump float; +#endif attribute vec2 vert; attribute vec2 tex_coord; uniform mat3 transform; uniform vec2 pixel_size; -uniform vec2 half_image_size; - -// varying vec2 uv1; -// varying vec2 uv2; varying vec2 top_left_uv; varying vec2 top_right_uv; varying vec2 bottom_left_uv; varying vec2 bottom_right_uv; -varying vec2 half_pixel_coord; void main() { top_left_uv = tex_coord - pixel_size / 2.0; @@ -22,11 +19,5 @@ void main() { top_right_uv = vec2(top_left_uv.x, bottom_right_uv.y); bottom_left_uv = vec2(bottom_right_uv.x, top_left_uv.y); - // uv1 = tex_coord - pixel_size / 2.0; - // uv2 = tex_coord + pixel_size / 2.0; - // uv1 += pixel_size; - // uv2 += pixel_size; - half_pixel_coord = top_left_uv * half_image_size; - gl_Position = vec4(transform * vec3(vert, 1), 1); } diff --git a/folder-symbolic.svg b/data/folder-symbolic.svg similarity index 100% rename from folder-symbolic.svg rename to data/folder-symbolic.svg diff --git a/data/meson.build b/data/meson.build new file mode 100644 index 0000000..c0de055 --- /dev/null +++ b/data/meson.build @@ -0,0 +1,15 @@ +resources = gnome.compile_resources('megapixels-resources', + 'org.postmarketos.Megapixels.gresource.xml') + +install_data(['org.postmarketos.Megapixels.desktop'], + install_dir: get_option('datadir') / 'applications') + +install_data(['org.postmarketos.Megapixels.metainfo.xml'], + install_dir: get_option('datadir') / 'metainfo') + +install_data('org.postmarketos.Megapixels.svg', + install_dir: join_paths(get_option('datadir'), 'icons/hicolor/scalable/apps')) + +install_data(['postprocess.sh'], + install_dir: get_option('datadir') / 'megapixels/', + install_mode: 'rwxr-xr-x') diff --git a/data/org.postmarketos.Megapixels.gresource.xml b/data/org.postmarketos.Megapixels.gresource.xml new file mode 100644 index 0000000..c71c8db --- /dev/null +++ b/data/org.postmarketos.Megapixels.gresource.xml @@ -0,0 +1,15 @@ + + + + camera.ui + camera.css + switch-camera.svg + shutter-button.svg + folder-symbolic.svg + settings-symbolic.svg + blit.vert + blit.frag + debayer.vert + debayer.frag + + diff --git a/postprocess.sh b/data/postprocess.sh similarity index 100% rename from postprocess.sh rename to data/postprocess.sh diff --git a/settings-symbolic.svg b/data/settings-symbolic.svg similarity index 100% rename from settings-symbolic.svg rename to data/settings-symbolic.svg diff --git a/shutter-button.svg b/data/shutter-button.svg similarity index 100% rename from shutter-button.svg rename to data/shutter-button.svg diff --git a/switch-camera.svg b/data/switch-camera.svg similarity index 100% rename from switch-camera.svg rename to data/switch-camera.svg diff --git a/gl_quickpreview.c b/gl_quickpreview.c deleted file mode 100644 index 38df81e..0000000 --- a/gl_quickpreview.c +++ /dev/null @@ -1,207 +0,0 @@ -#include "camera.h" -#include "gl_quickpreview.h" -#include "gl_utils.h" -#include - - -#define VERTEX_ATTRIBUTE 0 -#define TEX_COORD_ATTRIBUTE 1 - -struct _GLQuickPreview { - GLuint frame_buffer; - GLuint program; - GLuint uniform_transform; - GLuint uniform_pixel_size; - GLuint uniform_half_image_size; - GLuint uniform_texture; - GLuint uniform_pixel_layout; - GLuint uniform_srgb_map; - GLuint uniform_color_matrix; - - GLuint pixel_layout_texture_id; - uint32_t pixel_layout_texture_width; - uint32_t pixel_layout_texture_height; - MPPixelFormat pixel_layout_texture_format; -}; - -GLQuickPreview *gl_quick_preview_new() -{ - GLuint frame_buffer; - glGenFramebuffers(1, &frame_buffer); - check_gl(); - - GLuint shaders[] = { - gl_load_shader("data/debayer.vert", GL_VERTEX_SHADER), - gl_load_shader("data/debayer.frag", GL_FRAGMENT_SHADER), - }; - - GLuint program = gl_link_program(shaders, 2); - glBindAttribLocation(program, VERTEX_ATTRIBUTE, "vert"); - glBindAttribLocation(program, TEX_COORD_ATTRIBUTE, "tex_coord"); - check_gl(); - - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - check_gl(); - - GLuint pixel_layout_texture_id; - glGenTextures(1, &pixel_layout_texture_id); - check_gl(); - - GLQuickPreview *self = malloc(sizeof(GLQuickPreview)); - self->frame_buffer = frame_buffer; - self->program = program; - - self->uniform_transform = glGetUniformLocation(self->program, "transform"); - self->uniform_pixel_size = glGetUniformLocation(self->program, "pixel_size"); - self->uniform_half_image_size = glGetUniformLocation(self->program, "half_image_size"); - self->uniform_texture = glGetUniformLocation(self->program, "texture"); - self->uniform_pixel_layout = glGetUniformLocation(self->program, "pixel_layout"); - self->uniform_color_matrix = glGetUniformLocation(self->program, "color_matrix"); - self->uniform_srgb_map = glGetUniformLocation(self->program, "srgb_map"); - check_gl(); - - self->pixel_layout_texture_id = pixel_layout_texture_id; - self->pixel_layout_texture_width = 0; - self->pixel_layout_texture_height = 0; - self->pixel_layout_texture_format = MP_PIXEL_FMT_BGGR8; - - return self; -} - -void gl_quick_preview_free(GLQuickPreview *self) -{ - glDeleteFramebuffers(1, &self->frame_buffer); - - free(self); -} - -bool -ql_quick_preview_supports_format(GLQuickPreview *self, const MPPixelFormat format) -{ - return format == MP_PIXEL_FMT_BGGR8; -} - -bool -gl_quick_preview(GLQuickPreview *self, - GLuint dst_id, - const uint32_t dst_width, const uint32_t dst_height, - GLuint source_id, - const uint32_t src_width, const uint32_t src_height, - const MPPixelFormat format, - const uint32_t rotation, - const bool mirrored, - const float *colormatrix, - const uint8_t blacklevel) -{ - // Generate pixel layout image - if (src_width != self->pixel_layout_texture_width - || src_height != self->pixel_layout_texture_height - || format != self->pixel_layout_texture_format) { - - glBindTexture(GL_TEXTURE_2D, self->pixel_layout_texture_id); - - uint16_t *buffer = malloc(src_width * src_height * sizeof(uint16_t)); - for (size_t y = 0; y < src_height; ++y) { - for (size_t x = 0; x < src_width; ++x) { - buffer[x + y * src_width] = - (y % 2) == 0 - ? ((x % 2) == 0 ? 0b1111 << 4 : 0b1111 << 8) - : ((x % 2) == 0 ? 0b1111 << 8 : 0b1111 << 12); - } - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_width, src_height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, buffer); - check_gl(); - free(buffer); - - self->pixel_layout_texture_width = src_width; - self->pixel_layout_texture_height = src_height; - self->pixel_layout_texture_format = format; - glBindTexture(GL_TEXTURE_2D, 0); - check_gl(); - } - - assert(ql_quick_preview_supports_format(self, format)); - - glBindFramebuffer(GL_FRAMEBUFFER, self->frame_buffer); - glBindTexture(GL_TEXTURE_2D, dst_id); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_id, 0); - check_gl(); - - glViewport(0, 0, dst_width, dst_height); - check_gl(); - - assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); - - glUseProgram(self->program); - check_gl(); - - GLfloat rotation_list[4] = { 0, -1, 0, 1 }; - int rotation_index = 4 - rotation / 90; - - GLfloat sin_rot = rotation_list[rotation_index]; - GLfloat cos_rot = rotation_list[(rotation_index + 1) % 4]; - GLfloat scale_x = mirrored ? 1 : -1; - GLfloat matrix[9] = { - cos_rot * scale_x, sin_rot, 0, - -sin_rot * scale_x, cos_rot, 0, - 0, 0, 1, - }; - glUniformMatrix3fv(self->uniform_transform, 1, GL_FALSE, matrix); - check_gl(); - - glUniform2f(self->uniform_half_image_size, src_width / 2, src_height / 2); - check_gl(); - - GLfloat pixel_size_x = 1.0f / src_width; - GLfloat pixel_size_y = 1.0f / src_height; - glUniform2f(self->uniform_pixel_size, pixel_size_x, pixel_size_y); - check_gl(); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, source_id); - glUniform1i(self->uniform_texture, 0); - check_gl(); - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, self->pixel_layout_texture_id); - glUniform1i(self->uniform_pixel_layout, 1); - check_gl(); - - if (colormatrix) - { - GLfloat transposed[9]; - for (int i = 0; i < 3; ++i) - for (int j = 0; j < 3; ++j) - transposed[i + j * 3] = colormatrix[j + i * 3]; - - glUniformMatrix3fv(self->uniform_color_matrix, 1, GL_FALSE, transposed); - } - else - { - static const GLfloat identity[9] = { - 1, 0, 0, - 0, 1, 0, - 0, 0, 1, - }; - glUniformMatrix3fv(self->uniform_color_matrix, 1, GL_FALSE, identity); - } - - glVertexAttribPointer(VERTEX_ATTRIBUTE, 2, GL_FLOAT, 0, 0, gl_quad_vertices); - glEnableVertexAttribArray(VERTEX_ATTRIBUTE); - glVertexAttribPointer(TEX_COORD_ATTRIBUTE, 2, GL_FLOAT, 0, 0, gl_quad_texcoords); - glEnableVertexAttribArray(TEX_COORD_ATTRIBUTE); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - check_gl(); - - // TODO: Render - // glClearColor(0, 0, 1, 0); - // glClear(GL_COLOR_BUFFER_BIT); - - // glBindTexture(GL_TEXTURE_2D, 0); - // glBindFramebuffer(GL_FRAMEBUFFER, 0); - - return true; -} diff --git a/gl_quickpreview.h b/gl_quickpreview.h deleted file mode 100644 index fede8c5..0000000 --- a/gl_quickpreview.h +++ /dev/null @@ -1,21 +0,0 @@ -#include "quickpreview.h" -#include -#include -#include - -typedef struct _GLQuickPreview GLQuickPreview; - -GLQuickPreview* gl_quick_preview_new(); -void gl_quick_preview_free(GLQuickPreview *self); - -bool ql_quick_preview_supports_format(GLQuickPreview *self, const MPPixelFormat format); -bool gl_quick_preview(GLQuickPreview *self, - GLuint dst_id, - const uint32_t dst_width, const uint32_t dst_height, - GLuint source_id, - const uint32_t src_width, const uint32_t src_height, - const MPPixelFormat format, - const uint32_t rotation, - const bool mirrored, - const float *colormatrix, - const uint8_t blacklevel); diff --git a/gl_utils.c b/gl_utils.c deleted file mode 100644 index f32bf98..0000000 --- a/gl_utils.c +++ /dev/null @@ -1,128 +0,0 @@ -#include "gl_utils.h" - -#include -#include -#include -#include - -void __check_gl(const char *file, int line) -{ - GLenum error = glGetError(); - - const char *name; - switch (error) { - case GL_NO_ERROR: - return; // no error - case GL_INVALID_ENUM: - name = "GL_INVALID_ENUM"; - break; - case GL_INVALID_VALUE: - name = "GL_INVALID_VALUE"; - break; - case GL_INVALID_OPERATION: - name = "GL_INVALID_OPERATION"; - break; - case GL_INVALID_FRAMEBUFFER_OPERATION: - name = "GL_INVALID_FRAMEBUFFER_OPERATION"; - break; - case GL_OUT_OF_MEMORY: - name = "GL_OUT_OF_MEMORY"; - break; - default: - name = "UNKNOWN ERROR!"; - break; - } - - printf("GL error at %s:%d - %s\n", file, line, name); -} - -const GLfloat gl_quad_vertices[] = { - -1, -1, - 1, -1, - -1, 1, - 1, 1, -}; - -const GLfloat gl_quad_texcoords[] = { - 0, 0, - 1, 0, - 0, 1, - 1, 1, -}; - -GLuint -gl_load_shader(const char *path, GLenum type) -{ - check_gl(); - - FILE *f = fopen(path, "r"); - - fseek(f, 0, SEEK_END); - GLint size = ftell(f); - char *source = malloc(sizeof(char) * size); - - fseek(f, 0, SEEK_SET); - if (fread(source, size, 1, f) == 0) { - printf("Failed to read shader file\n"); - return 0; - } - - GLuint shader = glCreateShader(type); - assert(shader != 0); - glShaderSource(shader, 1, (const GLchar * const*)&source, &size); - glCompileShader(shader); - check_gl(); - - // Check compile status - GLint success; - glGetShaderiv(shader, GL_COMPILE_STATUS, &success); - if (success == GL_FALSE) { - printf("Shader compilation failed for %s\n", path); - } - - GLint log_length; - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); - if (log_length > 0) { - char *log = malloc(sizeof(char) * log_length); - glGetShaderInfoLog(shader, log_length - 1, &log_length, log); - - printf("Shader %s log: %s\n", path, log); - free(log); - } - - free(source); - - return shader; -} - -GLuint -gl_link_program(GLuint *shaders, size_t num_shaders) -{ - GLuint program = glCreateProgram(); - - for (size_t i = 0; i < num_shaders; ++i) { - glAttachShader(program, shaders[i]); - } - - glLinkProgram(program); - check_gl(); - - GLint success; - glGetProgramiv(program, GL_LINK_STATUS, &success); - if (success == GL_FALSE) { - printf("Program linking failed\n"); - } - - GLint log_length; - glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length); - if (log_length > 0) { - char *log = malloc(sizeof(char) * log_length); - glGetProgramInfoLog(program, log_length - 1, &log_length, log); - - printf("Program log: %s\n", log); - free(log); - } - check_gl(); - - return program; -} diff --git a/gl_utils.h b/gl_utils.h deleted file mode 100644 index 097c86a..0000000 --- a/gl_utils.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include -#include - -#define check_gl() __check_gl(__FILE__, __LINE__) -void __check_gl(const char *file, int line); - -extern const GLfloat gl_quad_vertices[8]; -extern const GLfloat gl_quad_texcoords[8]; - -GLuint gl_load_shader(const char *path, GLenum type); -GLuint gl_link_program(GLuint *shaders, size_t num_shaders); diff --git a/meson.build b/meson.build index ef331a6..163fc20 100644 --- a/meson.build +++ b/meson.build @@ -1,78 +1,76 @@ project('megapixels', 'c') + gnome = import('gnome') gtkdep = dependency('gtk4') tiff = dependency('libtiff-4') zbar = dependency('zbar') threads = dependency('threads') -gl = dependency('gl') -egl = dependency('egl') +# gl = dependency('gl') +epoxy = dependency('epoxy') cc = meson.get_compiler('c') libm = cc.find_library('m', required: false) -resources = gnome.compile_resources('megapixels-resources', 'org.postmarketos.Megapixels.gresource.xml') +subdir('data') conf = configuration_data() conf.set_quoted('DATADIR', join_paths(get_option('prefix'), get_option('datadir'))) conf.set_quoted('SYSCONFDIR', get_option('sysconfdir')) configure_file( output: 'config.h', - configuration: conf ) + configuration: conf) # Define DEBUG for debug builds only (debugoptimized is not included on this one) if get_option('buildtype') == 'debug' add_global_arguments('-DDEBUG', language: 'c') endif -# Workaround for libtiff having ABI changes but not changing the internal version number +# Workaround for libtiff having ABI changes but not changing the internal +# version number if get_option('tiffcfapattern') add_global_arguments('-DLIBTIFF_CFA_PATTERN', language: 'c') endif executable('megapixels', - 'main.c', - 'ini.c', - 'quickpreview.c', - 'gl_quickpreview.c', - 'gl_utils.c', - 'camera.c', - 'device.c', - 'pipeline.c', - 'camera_config.c', - 'io_pipeline.c', - 'process_pipeline.c', - 'zbar_pipeline.c', - 'matrix.c', - resources, - dependencies : [gtkdep, libm, tiff, zbar, threads, gl, egl], - install : true, - link_args : '-Wl,-ldl') + 'src/main.c', + 'src/ini.c', + 'src/gles2_debayer.c', + 'src/gl_util.c', + 'src/camera.c', + 'src/device.c', + 'src/pipeline.c', + 'src/camera_config.c', + 'src/io_pipeline.c', + 'src/process_pipeline.c', + 'src/zbar_pipeline.c', + 'src/matrix.c', + resources, + include_directories: 'src/', + dependencies: [gtkdep, libm, tiff, zbar, threads, epoxy], + install: true, + link_args: '-Wl,-ldl') -install_data(['data/org.postmarketos.Megapixels.desktop'], - install_dir : get_option('datadir') / 'applications') - -install_data(['data/org.postmarketos.Megapixels.metainfo.xml'], - install_dir : get_option('datadir') / 'metainfo') - -install_data('data/org.postmarketos.Megapixels.svg', - install_dir: join_paths(get_option('datadir'), 'icons/hicolor/scalable/apps') -) - -install_data([ - 'config/pine64,pinephone-1.0.ini', - 'config/pine64,pinephone-1.1.ini', - 'config/pine64,pinephone-1.2.ini', - 'config/pine64,pinetab.ini', +install_data( + [ + 'config/pine64,pinephone-1.0.ini', + 'config/pine64,pinephone-1.1.ini', + 'config/pine64,pinephone-1.2.ini', + 'config/pine64,pinetab.ini', ], - install_dir : get_option('datadir') / 'megapixels/config/') - -install_data(['postprocess.sh'], - install_dir : get_option('datadir') / 'megapixels/', - install_mode: 'rwxr-xr-x') + install_dir: get_option('datadir') / 'megapixels/config/') # Tools -executable('megapixels-list-devices', 'tools/list_devices.c', 'device.c', dependencies: [gtkdep], install: true) -executable('megapixels-camera-test', 'tools/camera_test.c', 'camera.c', 'device.c', dependencies: [gtkdep], install: true) +executable('megapixels-list-devices', + 'tools/list_devices.c', + 'src/device.c', + include_directories: 'src/', + dependencies: [gtkdep], + install: true) -test_quickpreview = executable('test_quickpreview', 'tests/test_quickpreview.c', 'quickpreview.c', 'camera.c', dependencies: [gtkdep]) -test('quickpreview', test_quickpreview) +executable('megapixels-camera-test', + 'tools/camera_test.c', + 'src/camera.c', + 'src/device.c', + include_directories: 'src/', + dependencies: [gtkdep], + install: true) diff --git a/org.postmarketos.Megapixels.gresource.xml b/org.postmarketos.Megapixels.gresource.xml deleted file mode 100644 index d9938c1..0000000 --- a/org.postmarketos.Megapixels.gresource.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - camera.glade - camera.css - switch-camera.svg - shutter-button.svg - folder-symbolic.svg - settings-symbolic.svg - - diff --git a/quickpreview.c b/quickpreview.c deleted file mode 100644 index 541b62e..0000000 --- a/quickpreview.c +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Fast but bad debayer method that scales and rotates by skipping source - * pixels and doesn't interpolate any values at all - */ - -#include "quickpreview.h" -#include -#include - -/* Linear -> sRGB lookup table */ -static const uint8_t srgb[] = { - 0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70, - 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, 96, 97, 99, 100, - 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, - 120, 121, 122, 123, 124, 125, 126, 127, 129, 130, 131, 132, 133, 134, - 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, - 148, 149, 150, 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, - 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, 170, - 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180, - 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, - 190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, - 199, 199, 200, 201, 201, 202, 202, 203, 204, 204, 205, 205, 206, 206, - 207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, - 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, - 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, 228, 229, 229, - 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, - 237, 237, 237, 238, 238, 239, 239, 240, 240, 241, 241, 242, 242, 243, - 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, - 250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255 -}; - -static inline uint32_t -pack_rgb(uint8_t r, uint8_t g, uint8_t b) -{ - return (r << 16) | (g << 8) | b; -} - -static inline uint32_t -convert_yuv_to_srgb(uint8_t y, uint8_t u, uint8_t v) -{ - uint32_t r = 1.164f * y + 1.596f * (v - 128); - uint32_t g = 1.164f * y - 0.813f * (v - 128) - 0.391f * (u - 128); - uint32_t b = 1.164f * y + 2.018f * (u - 128); - return pack_rgb(r, g, b); -} - -static inline uint32_t -apply_colormatrix(uint32_t color, const float *colormatrix) -{ - if (!colormatrix) { - return color; - } - - uint32_t r = (color >> 16) * colormatrix[0] + - ((color >> 8) & 0xFF) * colormatrix[1] + - (color & 0xFF) * colormatrix[2]; - uint32_t g = (color >> 16) * colormatrix[3] + - ((color >> 8) & 0xFF) * colormatrix[4] + - (color & 0xFF) * colormatrix[5]; - uint32_t b = (color >> 16) * colormatrix[6] + - ((color >> 8) & 0xFF) * colormatrix[7] + - (color & 0xFF) * colormatrix[8]; - - // Clip colors - if (r > 0xFF) - r = 0xFF; - if (g > 0xFF) - g = 0xFF; - if (b > 0xFF) - b = 0xFF; - return pack_rgb(r, g, b); -} - -static inline uint32_t -coord_map(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int rotation, - bool mirrored) -{ - uint32_t x_r, y_r; - if (rotation == 0) { - x_r = x; - y_r = y; - } else if (rotation == 90) { - x_r = y; - y_r = height - x - 1; - } else if (rotation == 270) { - x_r = width - y - 1; - y_r = x; - } else { - x_r = width - x - 1; - y_r = height - y - 1; - } - - if (mirrored) { - x_r = width - x_r - 1; - } - - uint32_t index = y_r * width + x_r; -#ifdef DEBUG - assert(index < width * height); -#endif - return index; -} - -static void -quick_preview_rggb8(uint32_t *dst, const uint32_t dst_width, - const uint32_t dst_height, const uint8_t *src, - const uint32_t src_width, const uint32_t src_height, - const MPPixelFormat format, const uint32_t rotation, - const bool mirrored, const float *colormatrix, - const uint8_t blacklevel, const uint32_t skip) -{ - uint32_t src_y = 0, dst_y = 0; - while (src_y < src_height) { - uint32_t src_x = 0, dst_x = 0; - while (src_x < src_width) { - uint32_t src_i = src_y * src_width + src_x; - - uint8_t b0 = srgb[src[src_i] - blacklevel]; - uint8_t b1 = srgb[src[src_i + 1] - blacklevel]; - uint8_t b2 = srgb[src[src_i + src_width + 1] - blacklevel]; - - uint32_t color; - switch (format) { - case MP_PIXEL_FMT_BGGR8: - color = pack_rgb(b2, b1, b0); - break; - case MP_PIXEL_FMT_GBRG8: - color = pack_rgb(b2, b0, b1); - break; - case MP_PIXEL_FMT_GRBG8: - color = pack_rgb(b1, b0, b2); - break; - case MP_PIXEL_FMT_RGGB8: - color = pack_rgb(b0, b1, b2); - break; - default: - assert(false); - } - - color = apply_colormatrix(color, colormatrix); - - dst[coord_map(dst_x, dst_y, dst_width, dst_height, rotation, - mirrored)] = color; - - src_x += 2 + 2 * skip; - ++dst_x; - } - - src_y += 2 + 2 * skip; - ++dst_y; - } -} - -static void -quick_preview_rggb10(uint32_t *dst, const uint32_t dst_width, - const uint32_t dst_height, const uint8_t *src, - const uint32_t src_width, const uint32_t src_height, - const MPPixelFormat format, const uint32_t rotation, - const bool mirrored, const float *colormatrix, - const uint8_t blacklevel, const uint32_t skip) -{ - assert(src_width % 2 == 0); - - uint32_t width_bytes = mp_pixel_format_width_to_bytes(format, src_width); - - uint32_t src_y = 0, dst_y = 0; - while (src_y < src_height) { - uint32_t src_x = 0, dst_x = 0; - while (src_x < width_bytes) { - uint32_t src_i = src_y * width_bytes + src_x; - - uint8_t b0 = srgb[src[src_i] - blacklevel]; - uint8_t b1 = srgb[src[src_i + 1] - blacklevel]; - uint8_t b2 = srgb[src[src_i + width_bytes + 1] - blacklevel]; - - uint32_t color; - switch (format) { - case MP_PIXEL_FMT_BGGR10P: - color = pack_rgb(b2, b1, b0); - break; - case MP_PIXEL_FMT_GBRG10P: - color = pack_rgb(b2, b0, b1); - break; - case MP_PIXEL_FMT_GRBG10P: - color = pack_rgb(b1, b0, b2); - break; - case MP_PIXEL_FMT_RGGB10P: - color = pack_rgb(b0, b1, b2); - break; - default: - assert(false); - } - - color = apply_colormatrix(color, colormatrix); - - dst[coord_map(dst_x, dst_y, dst_width, dst_height, rotation, - mirrored)] = color; - - uint32_t advance = 1 + skip; - if (src_x % 5 == 0) { - src_x += 2 * (advance % 2) + 5 * (advance / 2); - } else { - src_x += 3 * (advance % 2) + 5 * (advance / 2); - } - ++dst_x; - } - - src_y += 2 + 2 * skip; - ++dst_y; - } -} - -static void -quick_preview_yuv(uint32_t *dst, const uint32_t dst_width, const uint32_t dst_height, - const uint8_t *src, const uint32_t src_width, - const uint32_t src_height, const MPPixelFormat format, - const uint32_t rotation, const bool mirrored, - const float *colormatrix, const uint32_t skip) -{ - assert(src_width % 2 == 0); - - uint32_t width_bytes = src_width * 2; - - uint32_t unrot_dst_width = dst_width; - if (rotation != 0 && rotation != 180) { - unrot_dst_width = dst_height; - } - - uint32_t src_y = 0, dst_y = 0; - while (src_y < src_height) { - uint32_t src_x = 0, dst_x = 0; - while (src_x < width_bytes) { - uint32_t src_i = src_y * width_bytes + src_x; - - uint8_t b0 = src[src_i]; - uint8_t b1 = src[src_i + 1]; - uint8_t b2 = src[src_i + 2]; - uint8_t b3 = src[src_i + 3]; - - uint32_t color1, color2; - switch (format) { - case MP_PIXEL_FMT_UYVY: - color1 = convert_yuv_to_srgb(b1, b0, b2); - color2 = convert_yuv_to_srgb(b3, b0, b2); - break; - case MP_PIXEL_FMT_YUYV: - color1 = convert_yuv_to_srgb(b0, b1, b3); - color2 = convert_yuv_to_srgb(b2, b1, b3); - break; - default: - assert(false); - } - - color1 = apply_colormatrix(color1, colormatrix); - color2 = apply_colormatrix(color2, colormatrix); - - uint32_t dst_i1 = coord_map(dst_x, dst_y, dst_width, - dst_height, rotation, mirrored); - dst[dst_i1] = color1; - ++dst_x; - - // The last pixel needs to be skipped if we have an odd un-rotated width - if (dst_x < unrot_dst_width) { - uint32_t dst_i2 = - coord_map(dst_x, dst_y, dst_width, - dst_height, rotation, mirrored); - dst[dst_i2] = color2; - ++dst_x; - } - - src_x += 4 + 4 * skip; - } - - src_y += 1 + skip; - ++dst_y; - } -} - -void -quick_preview(uint32_t *dst, const uint32_t dst_width, const uint32_t dst_height, - const uint8_t *src, const uint32_t src_width, - const uint32_t src_height, const MPPixelFormat format, - const uint32_t rotation, const bool mirrored, const float *colormatrix, - const uint8_t blacklevel, const uint32_t skip) -{ - switch (format) { - case MP_PIXEL_FMT_BGGR8: - case MP_PIXEL_FMT_GBRG8: - case MP_PIXEL_FMT_GRBG8: - case MP_PIXEL_FMT_RGGB8: - quick_preview_rggb8(dst, dst_width, dst_height, src, src_width, - src_height, format, rotation, mirrored, - colormatrix, blacklevel, skip); - break; - case MP_PIXEL_FMT_BGGR10P: - case MP_PIXEL_FMT_GBRG10P: - case MP_PIXEL_FMT_GRBG10P: - case MP_PIXEL_FMT_RGGB10P: - quick_preview_rggb10(dst, dst_width, dst_height, src, src_width, - src_height, format, rotation, mirrored, - colormatrix, blacklevel, skip); - break; - case MP_PIXEL_FMT_UYVY: - case MP_PIXEL_FMT_YUYV: - quick_preview_yuv(dst, dst_width, dst_height, src, src_width, - src_height, format, rotation, mirrored, - colormatrix, skip); - break; - default: - assert(false); - } -} - -static uint32_t -div_ceil(uint32_t x, uint32_t y) -{ - return x / y + !!(x % y); -} - -void -quick_preview_size(uint32_t *dst_width, uint32_t *dst_height, uint32_t *skip, - const uint32_t preview_width, const uint32_t preview_height, - const uint32_t src_width, const uint32_t src_height, - const MPPixelFormat format, const int rotation) -{ - uint32_t colors_x = mp_pixel_format_width_to_colors(format, src_width); - uint32_t colors_y = mp_pixel_format_height_to_colors(format, src_height); - - if (rotation != 0 && rotation != 180) { - uint32_t tmp = colors_x; - colors_x = colors_y; - colors_y = tmp; - } - - uint32_t scale_x = colors_x / preview_width; - uint32_t scale_y = colors_y / preview_height; - - if (scale_x > 0) - --scale_x; - if (scale_y > 0) - --scale_y; - *skip = scale_x > scale_y ? scale_x : scale_y; - - *dst_width = div_ceil(colors_x, (1 + *skip)); - if (*dst_width <= 0) - *dst_width = 1; - - *dst_height = div_ceil(colors_y, (1 + *skip)); - if (*dst_height <= 0) - *dst_height = 1; -} diff --git a/quickpreview.h b/quickpreview.h deleted file mode 100644 index 1815215..0000000 --- a/quickpreview.h +++ /dev/null @@ -1,14 +0,0 @@ -#include "camera.h" -#include - -void quick_preview(uint32_t *dst, const uint32_t dst_width, - const uint32_t dst_height, const uint8_t *src, - const uint32_t src_width, const uint32_t src_height, - const MPPixelFormat format, const uint32_t rotation, - const bool mirrored, const float *colormatrix, - const uint8_t blacklevel, const uint32_t skip); - -void quick_preview_size(uint32_t *dst_width, uint32_t *dst_height, uint32_t *skip, - const uint32_t preview_width, const uint32_t preview_height, - const uint32_t src_width, const uint32_t src_height, - const MPPixelFormat format, const int rotation); diff --git a/camera.c b/src/camera.c similarity index 100% rename from camera.c rename to src/camera.c diff --git a/camera.h b/src/camera.h similarity index 100% rename from camera.h rename to src/camera.h diff --git a/camera_config.c b/src/camera_config.c similarity index 100% rename from camera_config.c rename to src/camera_config.c diff --git a/camera_config.h b/src/camera_config.h similarity index 100% rename from camera_config.h rename to src/camera_config.h diff --git a/device.c b/src/device.c similarity index 100% rename from device.c rename to src/device.c diff --git a/device.h b/src/device.h similarity index 100% rename from device.h rename to src/device.h diff --git a/src/gl_util.c b/src/gl_util.c new file mode 100644 index 0000000..c4c76ca --- /dev/null +++ b/src/gl_util.c @@ -0,0 +1,220 @@ +#include "gl_util.h" + +#include +#include +#include +#include +#include +#include +#include + +void gl_util_check_error(const char *file, int line) +{ + GLenum error = glGetError(); + + const char *name; + switch (error) { + case GL_NO_ERROR: + return; // no error + case GL_INVALID_ENUM: + name = "GL_INVALID_ENUM"; + break; + case GL_INVALID_VALUE: + name = "GL_INVALID_VALUE"; + break; + case GL_INVALID_OPERATION: + name = "GL_INVALID_OPERATION"; + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + name = "GL_INVALID_FRAMEBUFFER_OPERATION"; + break; + case GL_OUT_OF_MEMORY: + name = "GL_OUT_OF_MEMORY"; + break; + default: + name = "UNKNOWN ERROR!"; + break; + } + + printf("GL error at %s:%d - %s\n", file, line, name); + + // raise(SIGTRAP); +} + +GLuint +gl_util_load_shader(const char *resource, GLenum type, const char **extra_sources, size_t num_extra) +{ + GdkGLContext *context = gdk_gl_context_get_current(); + assert(context); + + GLuint shader = glCreateShader(type); + if (shader == 0) { + return 0; + } + + GBytes *bytes = g_resources_lookup_data(resource, 0, NULL); + if (!bytes) { + printf("Failed to load shader resource %s\n", resource); + return 0; + } + + // Build #define for OpenGL context information + gboolean is_es = gdk_gl_context_get_use_es(context); + int major, minor; + gdk_gl_context_get_version(context, &major, &minor); + char context_info_buf[128]; + snprintf(context_info_buf, 128, "#define %s\n#define GL_%d\n#define GL_%d_%d\n", is_es ? "GL_ES" : "GL_NO_ES", major, major, minor); + + gsize glib_size = 0; + const GLchar *source = g_bytes_get_data(bytes, &glib_size); + if (glib_size == 0 || glib_size > INT_MAX) { + printf("Invalid size for resource\n"); + return 0; + } + + const GLchar **sources = malloc((num_extra + 1) * sizeof(GLchar *)); + GLint *sizes = malloc((num_extra + 1) * sizeof(GLint)); + + for (size_t i = 0; i < num_extra; ++i) { + sources[i] = extra_sources[i]; + sizes[i] = -1; + } + sources[num_extra] = source; + sizes[num_extra] = glib_size; + + glShaderSource(shader, num_extra + 1, sources, sizes); + glCompileShader(shader); + check_gl(); + + free(sources); + free(sizes); + + g_bytes_unref(bytes); + + // Check compile status + GLint success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (success == GL_FALSE) { + printf("Shader compilation failed for %s\n", resource); + + glDeleteShader(shader); + return 0; + } + + GLint log_length; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); + if (log_length > 0) { + char *log = malloc(sizeof(char) * log_length); + glGetShaderInfoLog(shader, log_length - 1, &log_length, log); + + printf("Shader %s log: %s\n", resource, log); + free(log); + + glDeleteShader(shader); + return 0; + } + + return shader; +} + +GLuint +gl_util_link_program(GLuint *shaders, size_t num_shaders) +{ + GLuint program = glCreateProgram(); + + for (size_t i = 0; i < num_shaders; ++i) { + glAttachShader(program, shaders[i]); + } + + glLinkProgram(program); + check_gl(); + + GLint success; + glGetProgramiv(program, GL_LINK_STATUS, &success); + if (success == GL_FALSE) { + printf("Program linking failed\n"); + } + + GLint log_length; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length); + if (log_length > 0) { + char *log = malloc(sizeof(char) * log_length); + glGetProgramInfoLog(program, log_length - 1, &log_length, log); + + printf("Program log: %s\n", log); + free(log); + } + check_gl(); + + return program; +} + +static const GLfloat quad_data[] = { + // Vertices + -1, -1, + 1, -1, + -1, 1, + 1, 1, + // Texcoords + 0, 0, + 1, 0, + 0, 1, + 1, 1, +}; + +GLuint gl_util_new_quad() +{ + GdkGLContext *context = gdk_gl_context_get_current(); + assert(context); + + if (gdk_gl_context_get_use_es(context)) { + return 0; + } else { + GLuint buffer; + glGenBuffers(1, &buffer); + + glBindBuffer(GL_ARRAY_BUFFER, buffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(quad_data), quad_data, GL_STATIC_DRAW); + check_gl(); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + check_gl(); + + return buffer; + } +} + +void gl_util_bind_quad(GLuint buffer) +{ + GdkGLContext *context = gdk_gl_context_get_current(); + assert(context); + + if (gdk_gl_context_get_use_es(context)) { + glVertexAttribPointer(GL_UTIL_VERTEX_ATTRIBUTE, 2, GL_FLOAT, 0, 0, quad_data); + check_gl(); + glEnableVertexAttribArray(GL_UTIL_VERTEX_ATTRIBUTE); + check_gl(); + + glVertexAttribPointer(GL_UTIL_TEX_COORD_ATTRIBUTE, 2, GL_FLOAT, 0, 0, quad_data + 8); + check_gl(); + glEnableVertexAttribArray(GL_UTIL_TEX_COORD_ATTRIBUTE); + check_gl(); + } else { + glBindBuffer(GL_ARRAY_BUFFER, buffer); + check_gl(); + + glVertexAttribPointer(GL_UTIL_VERTEX_ATTRIBUTE, 2, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(GL_UTIL_VERTEX_ATTRIBUTE); + check_gl(); + + glVertexAttribPointer(GL_UTIL_TEX_COORD_ATTRIBUTE, 2, GL_FLOAT, GL_FALSE, 0, (void*) (8 * sizeof(float))); + glEnableVertexAttribArray(GL_UTIL_TEX_COORD_ATTRIBUTE); + check_gl(); + } +} + +void gl_util_draw_quad(GLuint buffer) +{ + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + check_gl(); +} diff --git a/src/gl_util.h b/src/gl_util.h new file mode 100644 index 0000000..f1e8451 --- /dev/null +++ b/src/gl_util.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#define GL_UTIL_VERTEX_ATTRIBUTE 0 +#define GL_UTIL_TEX_COORD_ATTRIBUTE 1 + +#define check_gl() gl_util_check_error(__FILE__, __LINE__) +void gl_util_check_error(const char *file, int line); + +GLuint gl_util_load_shader(const char *resource, GLenum type, const char **extra_sources, size_t num_extra); +GLuint gl_util_link_program(GLuint *shaders, size_t num_shaders); + +GLuint gl_util_new_quad(); +void gl_util_bind_quad(GLuint buffer); +void gl_util_draw_quad(GLuint buffer); diff --git a/src/gles2_debayer.c b/src/gles2_debayer.c new file mode 100644 index 0000000..df31fc4 --- /dev/null +++ b/src/gles2_debayer.c @@ -0,0 +1,141 @@ +#include "gles2_debayer.h" + +#include "camera.h" +#include "gl_util.h" +#include + +#define VERTEX_ATTRIBUTE 0 +#define TEX_COORD_ATTRIBUTE 1 + +struct _GLES2Debayer { + GLuint frame_buffer; + GLuint program; + GLuint uniform_transform; + GLuint uniform_pixel_size; + GLuint uniform_texture; + GLuint uniform_color_matrix; + + GLuint quad; +}; + +GLES2Debayer * +gles2_debayer_new(MPPixelFormat format) +{ + if (format != MP_PIXEL_FMT_BGGR8) { + return NULL; + } + + GLuint frame_buffer; + glGenFramebuffers(1, &frame_buffer); + check_gl(); + + GLuint shaders[] = { + gl_util_load_shader("/org/postmarketos/Megapixels/debayer.vert", GL_VERTEX_SHADER, NULL, 0), + gl_util_load_shader("/org/postmarketos/Megapixels/debayer.frag", GL_FRAGMENT_SHADER, NULL, 0), + }; + + GLuint program = gl_util_link_program(shaders, 2); + glBindAttribLocation(program, VERTEX_ATTRIBUTE, "vert"); + glBindAttribLocation(program, TEX_COORD_ATTRIBUTE, "tex_coord"); + check_gl(); + + GLES2Debayer *self = malloc(sizeof(GLES2Debayer)); + self->frame_buffer = frame_buffer; + self->program = program; + + self->uniform_transform = glGetUniformLocation(self->program, "transform"); + self->uniform_pixel_size = glGetUniformLocation(self->program, "pixel_size"); + self->uniform_texture = glGetUniformLocation(self->program, "texture"); + self->uniform_color_matrix = glGetUniformLocation(self->program, "color_matrix"); + check_gl(); + + self->quad = gl_util_new_quad(); + + return self; +} + +void +gles2_debayer_free(GLES2Debayer *self) +{ + glDeleteFramebuffers(1, &self->frame_buffer); + + glDeleteProgram(self->program); + + free(self); +} + +void +gles2_debayer_use(GLES2Debayer *self) +{ + glUseProgram(self->program); + check_gl(); + + gl_util_bind_quad(self->quad); +} + +void +gles2_debayer_configure(GLES2Debayer *self, + const uint32_t dst_width, const uint32_t dst_height, + const uint32_t src_width, const uint32_t src_height, + const uint32_t rotation, + const bool mirrored, + const float *colormatrix, + const uint8_t blacklevel) +{ + glViewport(0, 0, dst_width, dst_height); + check_gl(); + + GLfloat rotation_list[4] = { 0, -1, 0, 1 }; + int rotation_index = 4 - rotation / 90; + + GLfloat sin_rot = rotation_list[rotation_index]; + GLfloat cos_rot = rotation_list[(rotation_index + 1) % 4]; + GLfloat scale_x = mirrored ? 1 : -1; + GLfloat matrix[9] = { + cos_rot * scale_x, sin_rot, 0, + -sin_rot * scale_x, cos_rot, 0, + 0, 0, 1, + }; + glUniformMatrix3fv(self->uniform_transform, 1, GL_FALSE, matrix); + check_gl(); + + GLfloat pixel_size_x = 1.0f / src_width; + GLfloat pixel_size_y = 1.0f / src_height; + glUniform2f(self->uniform_pixel_size, pixel_size_x, pixel_size_y); + check_gl(); + + if (colormatrix) { + GLfloat transposed[9]; + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + transposed[i + j * 3] = colormatrix[j + i * 3]; + + glUniformMatrix3fv(self->uniform_color_matrix, 1, GL_FALSE, transposed); + } else { + static const GLfloat identity[9] = { + 1, 0, 0, + 0, 1, 0, + 0, 0, 1, + }; + glUniformMatrix3fv(self->uniform_color_matrix, 1, GL_FALSE, identity); + } + check_gl(); +} + +void +gles2_debayer_process(GLES2Debayer *self, GLuint dst_id, GLuint source_id) +{ + glBindFramebuffer(GL_FRAMEBUFFER, self->frame_buffer); + glBindTexture(GL_TEXTURE_2D, dst_id); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_id, 0); + check_gl(); + + assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, source_id); + glUniform1i(self->uniform_texture, 0); + check_gl(); + + gl_util_draw_quad(self->quad); +} diff --git a/src/gles2_debayer.h b/src/gles2_debayer.h new file mode 100644 index 0000000..0028749 --- /dev/null +++ b/src/gles2_debayer.h @@ -0,0 +1,21 @@ +#include "camera.h" +#include "gl_util.h" +#include +#include + +typedef struct _GLES2Debayer GLES2Debayer; + +GLES2Debayer* gles2_debayer_new(MPPixelFormat format); +void gles2_debayer_free(GLES2Debayer *self); + +void gles2_debayer_use(GLES2Debayer *self); + +void gles2_debayer_configure(GLES2Debayer *self, + const uint32_t dst_width, const uint32_t dst_height, + const uint32_t src_width, const uint32_t src_height, + const uint32_t rotation, + const bool mirrored, + const float *colormatrix, + const uint8_t blacklevel); + +void gles2_debayer_process(GLES2Debayer *self, GLuint dst_id, GLuint source_id); diff --git a/ini.c b/src/ini.c similarity index 100% rename from ini.c rename to src/ini.c diff --git a/ini.h b/src/ini.h similarity index 100% rename from ini.h rename to src/ini.h diff --git a/io_pipeline.c b/src/io_pipeline.c similarity index 100% rename from io_pipeline.c rename to src/io_pipeline.c diff --git a/io_pipeline.h b/src/io_pipeline.h similarity index 100% rename from io_pipeline.h rename to src/io_pipeline.h diff --git a/main.c b/src/main.c similarity index 95% rename from main.c rename to src/main.c index 3e38311..fb3af54 100644 --- a/main.c +++ b/src/main.c @@ -18,9 +18,8 @@ #include #include #include -#include "gl_utils.h" +#include "gl_util.h" #include "camera_config.h" -#include "quickpreview.h" #include "io_pipeline.h" #include "process_pipeline.h" @@ -330,6 +329,7 @@ draw_controls() static GLuint blit_program; static GLuint blit_uniform_texture; +static GLuint quad; static void preview_realize(GtkGLArea *area) @@ -340,17 +340,27 @@ preview_realize(GtkGLArea *area) return; } + // Make a VAO for OpenGL + if (!gtk_gl_area_get_use_es(area)) { + GLuint vao; + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + check_gl(); + } + GLuint blit_shaders[] = { - gl_load_shader("data/blit.vert", GL_VERTEX_SHADER), - gl_load_shader("data/blit.frag", GL_FRAGMENT_SHADER), + gl_util_load_shader("/org/postmarketos/Megapixels/blit.vert", GL_VERTEX_SHADER, NULL, 0), + gl_util_load_shader("/org/postmarketos/Megapixels/blit.frag", GL_FRAGMENT_SHADER, NULL, 0), }; - blit_program = gl_link_program(blit_shaders, 2); - glBindAttribLocation(blit_program, 0, "vert"); - glBindAttribLocation(blit_program, 1, "tex_coord"); + blit_program = gl_util_link_program(blit_shaders, 2); + glBindAttribLocation(blit_program, GL_UTIL_VERTEX_ATTRIBUTE, "vert"); + glBindAttribLocation(blit_program, GL_UTIL_TEX_COORD_ATTRIBUTE, "tex_coord"); check_gl(); blit_uniform_texture = glGetUniformLocation(blit_program, "texture"); + + quad = gl_util_new_quad(); } static gboolean @@ -364,9 +374,9 @@ preview_draw(GtkGLArea *area, GdkGLContext *ctx, gpointer data) return FALSE; } -// #ifdef RENDERDOC -// if (rdoc_api) rdoc_api->StartFrameCapture(NULL, NULL); -// #endif +#ifdef RENDERDOC + if (rdoc_api) rdoc_api->StartFrameCapture(NULL, NULL); +#endif glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); @@ -377,13 +387,10 @@ preview_draw(GtkGLArea *area, GdkGLContext *ctx, gpointer data) glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, mp_process_pipeline_buffer_get_texture_id(current_preview_buffer)); glUniform1i(blit_uniform_texture, 0); - - glVertexAttribPointer(0, 2, GL_FLOAT, 0, 0, gl_quad_vertices); - glEnableVertexAttribArray(0); - glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, gl_quad_texcoords); - glEnableVertexAttribArray(1); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); check_gl(); + + gl_util_bind_quad(quad); + gl_util_draw_quad(quad); } /* @@ -433,9 +440,9 @@ preview_draw(GtkGLArea *area, GdkGLContext *ctx, gpointer data) glFlush(); -// #ifdef RENDERDOC -// if(rdoc_api) rdoc_api->EndFrameCapture(NULL, NULL); -// #endif +#ifdef RENDERDOC + if(rdoc_api) rdoc_api->EndFrameCapture(NULL, NULL); +#endif return FALSE; } @@ -758,6 +765,9 @@ on_realize(GtkWidget *window, gpointer *data) { GtkNative *native = gtk_widget_get_native(window); mp_process_pipeline_init_gl(gtk_native_get_surface(native)); + + camera = mp_get_camera_config(0); + update_io_pipeline(); } typedef struct @@ -778,13 +788,10 @@ startup(GApplication *app) g_object_set(gtk_settings_get_default(), "gtk-application-prefer-dark-theme", TRUE, NULL); - GtkBuilder *builder; - if (access("camera.ui", F_OK) != -1) { - builder = gtk_builder_new_from_file("camera.ui"); - } else { - builder = gtk_builder_new_from_file( - "/org/postmarketos/Megapixels/camera.ui"); - } + assert(g_resources_lookup_data("/org/postmarketos/Megapixels/camera.ui", 0, NULL) != NULL); + + GtkBuilder *builder = gtk_builder_new_from_resource( + "/org/postmarketos/Megapixels/camera.ui"); GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window")); GtkWidget *shutter = GTK_WIDGET(gtk_builder_get_object(builder, "shutter")); @@ -842,12 +849,8 @@ startup(GApplication *app) G_CALLBACK(on_control_slider_changed), NULL); GtkCssProvider *provider = gtk_css_provider_new(); - if (access("camera.css", F_OK) != -1) { - gtk_css_provider_load_from_path(provider, "camera.css"); - } else { - gtk_css_provider_load_from_resource( - provider, "/org/postmarketos/Megapixels/camera.css"); - } + gtk_css_provider_load_from_resource( + provider, "/org/postmarketos/Megapixels/camera.css"); GtkStyleContext *context = gtk_widget_get_style_context(error_box); gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_USER); @@ -868,9 +871,6 @@ startup(GApplication *app) mp_io_pipeline_start(); - camera = mp_get_camera_config(0); - update_io_pipeline(); - gtk_application_add_window(GTK_APPLICATION(app), GTK_WINDOW(window)); gtk_widget_show(window); } diff --git a/main.h b/src/main.h similarity index 100% rename from main.h rename to src/main.h diff --git a/matrix.c b/src/matrix.c similarity index 100% rename from matrix.c rename to src/matrix.c diff --git a/matrix.h b/src/matrix.h similarity index 100% rename from matrix.h rename to src/matrix.h diff --git a/pipeline.c b/src/pipeline.c similarity index 100% rename from pipeline.c rename to src/pipeline.c diff --git a/pipeline.h b/src/pipeline.h similarity index 100% rename from pipeline.h rename to src/pipeline.h diff --git a/process_pipeline.c b/src/process_pipeline.c similarity index 85% rename from process_pipeline.c rename to src/process_pipeline.c index 8e178ff..e6dc53a 100644 --- a/process_pipeline.c +++ b/src/process_pipeline.c @@ -5,18 +5,14 @@ #include "io_pipeline.h" #include "main.h" #include "config.h" -#include "quickpreview.h" -#include "gl_quickpreview.h" +#include "gles2_debayer.h" #include #include #include #include #include -#include "gl_utils.h" -#include -#include -// #include +#include "gl_util.h" #include #include @@ -82,9 +78,9 @@ find_processor(char *script) wordfree(&exp_result); // Check postprocess.h in the current working directory - sprintf(script, "%s", filename); + sprintf(script, "data/%s", filename); if (access(script, F_OK) != -1) { - sprintf(script, "./%s", filename); + sprintf(script, "./data/%s", filename); printf("Found postprocessor script at %s\n", script); return true; } @@ -158,9 +154,6 @@ struct _MPProcessPipelineBuffer { }; static MPProcessPipelineBuffer output_buffers[NUM_BUFFERS]; -static int output_buffer_width = 0; -static int output_buffer_height = 0; - void mp_process_pipeline_buffer_ref(MPProcessPipelineBuffer *buf) { @@ -179,37 +172,10 @@ mp_process_pipeline_buffer_get_texture_id(MPProcessPipelineBuffer *buf) return buf->texture_id; } -static GLQuickPreview *gl_quick_preview_state = NULL; +static GLES2Debayer *gles2_debayer = NULL; static GdkGLContext *context; -static PFNEGLEXPORTDMABUFIMAGEMESAPROC eglExportDMABUFImageMESA; -static PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC eglExportDMABUFImageQueryMESA; - -// static const char * -// egl_get_error_str() -// { -// EGLint error = eglGetError(); -// switch (error) { -// case EGL_SUCCESS: return "EGL_SUCCESS"; -// case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED"; -// case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS"; -// case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC"; -// case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE"; -// case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT"; -// case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG"; -// case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE"; -// case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; -// case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; -// case EGL_BAD_MATCH: return "EGL_BAD_MATCH"; -// case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER"; -// case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP"; -// case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW"; -// case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST"; -// } -// return "Unknown"; -// } - #define RENDERDOC #ifdef RENDERDOC @@ -230,6 +196,7 @@ init_gl(MPPipeline *pipeline, GdkSurface **surface) gdk_gl_context_set_use_es(context, true); gdk_gl_context_set_required_version(context, 2, 0); + gdk_gl_context_set_forward_compatible(context, false); #ifdef DEBUG gdk_gl_context_set_debug_enabled(context, true); #else @@ -247,29 +214,22 @@ init_gl(MPPipeline *pipeline, GdkSurface **surface) gdk_gl_context_make_current(context); check_gl(); - eglExportDMABUFImageMESA = (PFNEGLEXPORTDMABUFIMAGEMESAPROC) - eglGetProcAddress("eglExportDMABUFImageMESA"); - eglExportDMABUFImageQueryMESA = (PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC) - eglGetProcAddress("eglExportDMABUFImageQueryMESA"); + // Make a VAO for OpenGL + if (!gdk_gl_context_get_use_es(context)) { + GLuint vao; + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + check_gl(); + } - // Generate textures for the buffers - // GLuint textures[NUM_BUFFERS * 2]; - // glGenTextures(NUM_BUFFERS * 2, textures); - - // for (size_t i = 0; i < NUM_BUFFERS; ++i) { - // input_buffers[i].texture_id = textures[i]; - // input_buffers[i].egl_image = EGL_NO_IMAGE; - // input_buffers[i].dma_fd = -1; - // } - // for (size_t i = 0; i < NUM_BUFFERS; ++i) { - // output_buffers[i].texture_id = textures[NUM_BUFFERS + i]; - // output_buffers[i].egl_image = EGL_NO_IMAGE; - // output_buffers[i].dma_fd = -1; - // } - - gl_quick_preview_state = gl_quick_preview_new(); + gles2_debayer = gles2_debayer_new(MP_PIXEL_FMT_BGGR8); check_gl(); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + check_gl(); + + gles2_debayer_use(gles2_debayer); + for (size_t i = 0; i < NUM_BUFFERS; ++i) { glGenTextures(1, &output_buffers[i].texture_id); glBindTexture(GL_TEXTURE_2D, output_buffers[i].texture_id); @@ -279,7 +239,11 @@ init_gl(MPPipeline *pipeline, GdkSurface **surface) glBindTexture(GL_TEXTURE_2D, 0); - printf("Initialized OpenGL\n"); + gboolean is_es = gdk_gl_context_get_use_es(context); + int major, minor; + gdk_gl_context_get_version(context, &major, &minor); + + printf("Initialized %s %d.%d\n", is_es ? "OpenGL ES" : "OpenGL", major, minor); } void @@ -293,8 +257,6 @@ process_image_for_preview(const uint8_t *image) { clock_t t1 = clock(); - assert(gl_quick_preview_state && ql_quick_preview_supports_format(gl_quick_preview_state, mode.pixel_format)); - // Pick an available buffer MPProcessPipelineBuffer *output_buffer = NULL; for (size_t i = 0; i < NUM_BUFFERS; ++i) { @@ -311,8 +273,6 @@ process_image_for_preview(const uint8_t *image) #ifdef RENDERDOC if (rdoc_api) rdoc_api->StartFrameCapture(NULL, NULL); #endif - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - check_gl(); // Copy image to a GL texture. TODO: This can be avoided GLuint input_texture; @@ -325,24 +285,13 @@ process_image_for_preview(const uint8_t *image) glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, mode.width, mode.height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image); check_gl(); - - gl_quick_preview( - gl_quick_preview_state, - output_buffer->texture_id, output_buffer_width, output_buffer_height, - input_texture, mode.width, mode.height, - mode.pixel_format, - camera->rotate, camera->mirrored, - camera->previewmatrix[0] == 0 ? NULL : camera->previewmatrix, - camera->blacklevel); + gles2_debayer_process( + gles2_debayer, output_buffer->texture_id, input_texture); check_gl(); - // surface = cairo_image_surface_create( - // CAIRO_FORMAT_RGB24, preview_width, preview_height); - // uint32_t *pixels = (uint32_t *)cairo_image_surface_get_data(surface); glFinish(); - glBindTexture(GL_TEXTURE_2D, 0); - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteTextures(1, &input_texture); clock_t t2 = clock(); printf("%fms\n", (float)(t2 - t1) / CLOCKS_PER_SEC * 1000); @@ -727,10 +676,10 @@ mp_process_pipeline_capture() } static void -update_output_buffers() +on_output_changed() { - output_buffer_width = mode.width / 2; - output_buffer_height = mode.height / 2; + int output_buffer_width = mode.width / 2; + int output_buffer_height = mode.height / 2; if (camera->rotate != 0 || camera->rotate != 180) { int tmp = output_buffer_width; @@ -744,12 +693,20 @@ update_output_buffers() } glBindTexture(GL_TEXTURE_2D, 0); + + gles2_debayer_configure( + gles2_debayer, + output_buffer_width, output_buffer_height, + mode.width, mode.height, + camera->rotate, camera->mirrored, + camera->previewmatrix[0] == 0 ? NULL : camera->previewmatrix, + camera->blacklevel); } static void update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state) { - const bool buffer_update_required = (!mp_camera_mode_is_equivalent(&mode, &state->mode) || preview_width != state->preview_width || preview_height != state->preview_height); + const bool output_changed = (!mp_camera_mode_is_equivalent(&mode, &state->mode) || preview_width != state->preview_width || preview_height != state->preview_height); camera = state->camera; mode = state->mode; @@ -766,8 +723,8 @@ update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state exposure_is_manual = state->exposure_is_manual; exposure = state->exposure; - if (buffer_update_required) { - update_output_buffers(); + if (output_changed) { + on_output_changed(); } struct mp_main_state main_state = { diff --git a/process_pipeline.h b/src/process_pipeline.h similarity index 100% rename from process_pipeline.h rename to src/process_pipeline.h diff --git a/renderdoc/app.h b/src/renderdoc/app.h similarity index 100% rename from renderdoc/app.h rename to src/renderdoc/app.h diff --git a/zbar_pipeline.c b/src/zbar_pipeline.c similarity index 100% rename from zbar_pipeline.c rename to src/zbar_pipeline.c diff --git a/zbar_pipeline.h b/src/zbar_pipeline.h similarity index 100% rename from zbar_pipeline.h rename to src/zbar_pipeline.h diff --git a/tests/test_quickpreview.c b/tests/test_quickpreview.c deleted file mode 100644 index 9b43778..0000000 --- a/tests/test_quickpreview.c +++ /dev/null @@ -1,120 +0,0 @@ -#include "quickpreview.h" -#include -#include -#include - -static void -test_quick_preview_fuzz() -{ - for (size_t i = 0; i < 10000; ++i) { - uint32_t width = rand() % 127 + 1; - uint32_t height = rand() % 127 + 1; - uint32_t format = rand() % (MP_PIXEL_FMT_MAX - 1) + 1; - - assert(width > 0 && height > 0); - - switch (format) { - case MP_PIXEL_FMT_BGGR8: - case MP_PIXEL_FMT_GBRG8: - case MP_PIXEL_FMT_GRBG8: - case MP_PIXEL_FMT_RGGB8: - width += width % 2; - height += height % 2; - break; - case MP_PIXEL_FMT_BGGR10P: - case MP_PIXEL_FMT_GBRG10P: - case MP_PIXEL_FMT_GRBG10P: - case MP_PIXEL_FMT_RGGB10P: - width += width % 2; - height += height % 2; - break; - case MP_PIXEL_FMT_UYVY: - case MP_PIXEL_FMT_YUYV: - width += width % 4; - break; - default: - assert(false); - } - - int rotation; - switch (rand() % 3) { - case 0: - rotation = 0; - break; - case 1: - rotation = 90; - break; - case 2: - rotation = 180; - break; - default: - rotation = 270; - break; - } - - bool mirrored = rand() % 2; - - float matbuf[9]; - float *colormatrix = NULL; - if (rand() % 2) { - for (int j = 0; j < 9; ++j) { - matbuf[j] = (double)rand() / RAND_MAX * 2.0; - } - colormatrix = matbuf; - } - - uint8_t blacklevel = rand() % 3; - - size_t src_size = mp_pixel_format_width_to_bytes(format, width) * height; - uint8_t *src = malloc(src_size); - for (int j = 0; j < src_size; ++j) { - src[j] = rand(); - } - - uint32_t preview_width = mp_pixel_format_width_to_colors(format, width); - uint32_t preview_height = mp_pixel_format_height_to_colors(format, height); - if (preview_width > 32 && preview_height > 32) { - preview_width /= (1 + rand() % 2); - preview_height /= (1 + rand() % 2); - } - - uint32_t dst_width, dst_height, skip; - quick_preview_size( - &dst_width, - &dst_height, - &skip, - preview_width, - preview_height, - width, - height, - format, - rotation); - - uint32_t *dst = malloc(dst_width * dst_height * sizeof(uint32_t)); - - quick_preview( - dst, - dst_width, - dst_height, - src, - width, - height, - format, - rotation, - mirrored, - colormatrix, - blacklevel, - skip); - - free(dst); - free(src); - } -} - -int -main(int argc, char *argv[]) -{ - g_test_init(&argc, &argv, NULL); - g_test_add_func("/quick_preview/fuzz", test_quick_preview_fuzz); - return g_test_run(); -}