diff --git a/camera.c b/camera.c index f6fbf37..6bc3e36 100644 --- a/camera.c +++ b/camera.c @@ -607,6 +607,7 @@ mp_camera_capture_buffer(MPCamera *camera, MPBuffer *buffer) /* fallthrough */ default: errno_printerr("VIDIOC_DQBUF"); + exit(1); return false; } } diff --git a/camera.css b/camera.css index e897a85..ace8c79 100644 --- a/camera.css +++ b/camera.css @@ -1,7 +1,3 @@ -.black { - background: #000000; -} - .errorbox { background: #dd0000; color: #ffffff; diff --git a/camera.glade b/camera.glade index 8fb7e67..89d7872 100644 --- a/camera.glade +++ b/camera.glade @@ -27,7 +27,7 @@ False vertical - + True False diff --git a/camera.ui b/camera.ui new file mode 100644 index 0000000..bb06696 --- /dev/null +++ b/camera.ui @@ -0,0 +1,335 @@ + + + + + 100 + 1 + 10 + + + 0 + 360 + 640 + 0 + + + 0 + + + main + + + 0 + vertical + + + 1 + 0 + + + + + 0 + 0 + + + 1 + 0 + 10 + 10 + 10 + 10 + + + 0 + 10 + ISO + + + + + 1 + control_adj + 1 + + + + + Auto + 1 + 10 + + + + + + + + + + 0 + + + 1 + + + 0 + /org/postmarketos/Megapixels/settings-symbolic.svg + + + + + + + 1 + + + 0 + /org/postmarketos/Megapixels/switch-camera.svg + + + + + + + 48 + 48 + 1 + + + 0 + /org/postmarketos/Megapixels/shutter-button.svg + + + + + + + + 1 + + + 0 + /org/postmarketos/Megapixels/folder-symbolic.svg + + + + + + + 1 + + + 0 + + + 24 + 24 + 0 + + + + + 24 + 24 + 0 + + + + + + + + + + + 0 + 0 + + + 1 + 0 + 10 + 10 + 10 + 10 + 10 + + + 1 + 0 + start + No error + + + + + gtk-close + 1 + + + + + + + + + + + + + + + + diff --git a/config/pine64,pinephone-1.0.ini b/config/pine64,pinephone-1.0.ini index cafbb77..b97fcc4 100644 --- a/config/pine64,pinephone-1.0.ini +++ b/config/pine64,pinephone-1.0.ini @@ -11,7 +11,7 @@ capture-rate=10 capture-fmt=BGGR8 preview-width=1280 preview-height=720 -preview-rate=20 +preview-rate=60 preview-fmt=BGGR8 rotate=270 colormatrix=1.384,-0.3203,-0.0124,-0.2728,1.049,0.1556,-0.0506,0.2577,0.8050 @@ -29,11 +29,11 @@ driver=gc2145 media-driver=sun6i-csi capture-width=1280 capture-height=960 -capture-rate=30 +capture-rate=60 capture-fmt=BGGR8 preview-width=1280 preview-height=960 -preview-rate=30 +preview-rate=60 preview-fmt=BGGR8 rotate=90 mirrored=true diff --git a/config/pine64,pinephone-1.1.ini b/config/pine64,pinephone-1.1.ini index cafbb77..b97fcc4 100644 --- a/config/pine64,pinephone-1.1.ini +++ b/config/pine64,pinephone-1.1.ini @@ -11,7 +11,7 @@ capture-rate=10 capture-fmt=BGGR8 preview-width=1280 preview-height=720 -preview-rate=20 +preview-rate=60 preview-fmt=BGGR8 rotate=270 colormatrix=1.384,-0.3203,-0.0124,-0.2728,1.049,0.1556,-0.0506,0.2577,0.8050 @@ -29,11 +29,11 @@ driver=gc2145 media-driver=sun6i-csi capture-width=1280 capture-height=960 -capture-rate=30 +capture-rate=60 capture-fmt=BGGR8 preview-width=1280 preview-height=960 -preview-rate=30 +preview-rate=60 preview-fmt=BGGR8 rotate=90 mirrored=true diff --git a/config/pine64,pinephone-1.2.ini b/config/pine64,pinephone-1.2.ini index 74ce9d2..bab05aa 100644 --- a/config/pine64,pinephone-1.2.ini +++ b/config/pine64,pinephone-1.2.ini @@ -11,7 +11,7 @@ capture-rate=10 capture-fmt=BGGR8 preview-width=1280 preview-height=720 -preview-rate=20 +preview-rate=60 preview-fmt=BGGR8 rotate=270 mirrored=false @@ -30,11 +30,11 @@ driver=gc2145 media-driver=sun6i-csi capture-width=1280 capture-height=960 -capture-rate=30 +capture-rate=60 capture-fmt=BGGR8 preview-width=1280 preview-height=960 -preview-rate=30 +preview-rate=60 preview-fmt=BGGR8 rotate=90 mirrored=true diff --git a/data/blit.frag b/data/blit.frag new file mode 100644 index 0000000..43ea399 --- /dev/null +++ b/data/blit.frag @@ -0,0 +1,11 @@ +precision mediump float; + +uniform sampler2D texture; + +varying vec2 uv; + +#define fetch(p) texture2D(texture, p).r + +void main() { + gl_FragColor = texture2D(texture, uv); +} diff --git a/data/blit.vert b/data/blit.vert new file mode 100644 index 0000000..b4576b8 --- /dev/null +++ b/data/blit.vert @@ -0,0 +1,12 @@ +precision mediump float; + +attribute vec2 vert; +attribute vec2 tex_coord; + +varying vec2 uv; + +void main() { + uv = tex_coord; + + gl_Position = vec4(vert, 0, 1); +} diff --git a/data/debayer.frag b/data/debayer.frag index 193168e..94ecc50 100644 --- a/data/debayer.frag +++ b/data/debayer.frag @@ -1,36 +1,43 @@ -precision mediump float; +precision highp float; uniform sampler2D texture; -// uniform sampler2D srgb_map; +uniform sampler2D pixel_layout; uniform mat3 color_matrix; -varying vec2 uv1; -varying vec2 uv2; -varying vec2 pixel_coord; +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(uv1), - fetch(vec2(uv1.x, uv2.y)), - fetch(uv2), - fetch(vec2(uv2.x, uv1.y))); + fetch(top_left_uv), + fetch(top_right_uv), + fetch(bottom_right_uv), + fetch(bottom_left_uv)); - vec2 arrangement = mod(pixel_coord, 2.0); + vec2 arrangement = floor(half_pixel_coord); - vec2 is_top_left = step(1.0, arrangement); + vec2 is_bottom_left = step(1.0, arrangement); - vec3 color = mix( - mix(pixels.zyx, pixels.wzy, is_top_left.y), - mix(pixels.yzw, pixels.xyz, is_top_left.y), - is_top_left.x); + // 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; - vec3 srgb_color = pow(color, vec3(1.0 / 2.2)); - // vec3 srgb_color = vec3( - // texture2D(srgb_map, vec2(color.r, 0)).r, - // texture2D(srgb_map, vec2(color.g, 0)).r, - // texture2D(srgb_map, vec2(color.b, 0)).r); + // Fast SRGB estimate. See https://mimosa-pudica.net/fast-gamma/ + vec3 srgb_color = (vec3(1.138) * inversesqrt(color) - vec3(0.138)) * color; - gl_FragColor = vec4((color_matrix * srgb_color).bgr, 0); + // Slow SRGB estimate + // vec3 srgb_color = pow(color, vec3(1.0 / 2.2)); + + gl_FragColor = vec4(color_matrix * srgb_color, 0); } diff --git a/data/debayer.vert b/data/debayer.vert index ac06880..d06d511 100644 --- a/data/debayer.vert +++ b/data/debayer.vert @@ -1,21 +1,32 @@ -precision mediump float; +precision highp float; 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 pixel_coord; +// 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() { - uv1 = tex_coord - pixel_size / 2.0; - uv2 = tex_coord + pixel_size / 2.0; - uv1 += pixel_size; - uv2 += pixel_size; - pixel_coord = uv1 / pixel_size; + top_left_uv = tex_coord - pixel_size / 2.0; + bottom_right_uv = tex_coord + pixel_size / 2.0; + 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/gl_quickpreview.c b/gl_quickpreview.c index 1f48f3a..38df81e 100644 --- a/gl_quickpreview.c +++ b/gl_quickpreview.c @@ -1,153 +1,51 @@ #include "camera.h" #include "gl_quickpreview.h" #include "gl_utils.h" -#include #include + #define VERTEX_ATTRIBUTE 0 #define TEX_COORD_ATTRIBUTE 1 -static const GLfloat square_vertices[] = { - -1, -1, - 1, -1, - -1, 1, - 1, 1, -}; - -static const GLfloat square_texcoords[] = { - 1, 1, - 1, 0, - 0, 1, - 0, 0, -}; - 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 srgb_texture; + + GLuint pixel_layout_texture_id; + uint32_t pixel_layout_texture_width; + uint32_t pixel_layout_texture_height; + MPPixelFormat pixel_layout_texture_format; }; -static GLuint 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; -} - -// 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 -// }; - GLQuickPreview *gl_quick_preview_new() { GLuint frame_buffer; glGenFramebuffers(1, &frame_buffer); check_gl(); - GLuint vert = load_shader("data/debayer.vert", GL_VERTEX_SHADER); - GLuint frag = load_shader("data/debayer.frag", GL_FRAGMENT_SHADER); - - GLuint program = glCreateProgram(); - glAttachShader(program, vert); - glAttachShader(program, frag); + 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"); - 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(); - - // GLuint srgb_texture; - // glGenTextures(1, &srgb_texture); - // check_gl(); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); check_gl(); - // glBindTexture(GL_TEXTURE_2D, srgb_texture); - // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - // check_gl(); - // glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 256, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, srgb); - // check_gl(); - // glBindTexture(GL_TEXTURE_2D, 0); + GLuint pixel_layout_texture_id; + glGenTextures(1, &pixel_layout_texture_id); + check_gl(); GLQuickPreview *self = malloc(sizeof(GLQuickPreview)); self->frame_buffer = frame_buffer; @@ -155,10 +53,18 @@ GLQuickPreview *gl_quick_preview_new() 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"); - // self->srgb_texture = srgb_texture; + 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; } @@ -187,6 +93,36 @@ gl_quick_preview(GLQuickPreview *self, 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); @@ -202,7 +138,7 @@ gl_quick_preview(GLQuickPreview *self, glUseProgram(self->program); check_gl(); - GLfloat rotation_list[4] = { 1, 0, -1, 0 }; + GLfloat rotation_list[4] = { 0, -1, 0, 1 }; int rotation_index = 4 - rotation / 90; GLfloat sin_rot = rotation_list[rotation_index]; @@ -216,6 +152,9 @@ gl_quick_preview(GLQuickPreview *self, 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); @@ -226,10 +165,10 @@ gl_quick_preview(GLQuickPreview *self, glUniform1i(self->uniform_texture, 0); check_gl(); - // glActiveTexture(GL_TEXTURE1); - // glBindTexture(GL_TEXTURE_2D, self->srgb_texture); - // glUniform1i(self->uniform_srgb_map, 1); - // check_gl(); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, self->pixel_layout_texture_id); + glUniform1i(self->uniform_pixel_layout, 1); + check_gl(); if (colormatrix) { @@ -250,14 +189,10 @@ gl_quick_preview(GLQuickPreview *self, glUniformMatrix3fv(self->uniform_color_matrix, 1, GL_FALSE, identity); } - glVertexAttribPointer(VERTEX_ATTRIBUTE, 2, GL_FLOAT, 0, 0, square_vertices); - check_gl(); + glVertexAttribPointer(VERTEX_ATTRIBUTE, 2, GL_FLOAT, 0, 0, gl_quad_vertices); glEnableVertexAttribArray(VERTEX_ATTRIBUTE); - check_gl(); - glVertexAttribPointer(TEX_COORD_ATTRIBUTE, 2, GL_FLOAT, 0, 0, square_texcoords); - check_gl(); + glVertexAttribPointer(TEX_COORD_ATTRIBUTE, 2, GL_FLOAT, 0, 0, gl_quad_texcoords); glEnableVertexAttribArray(TEX_COORD_ATTRIBUTE); - check_gl(); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); check_gl(); @@ -268,8 +203,5 @@ gl_quick_preview(GLQuickPreview *self, // glBindTexture(GL_TEXTURE_2D, 0); // glBindFramebuffer(GL_FRAMEBUFFER, 0); - glUseProgram(0); - check_gl(); - return true; } diff --git a/gl_utils.c b/gl_utils.c new file mode 100644 index 0000000..f32bf98 --- /dev/null +++ b/gl_utils.c @@ -0,0 +1,128 @@ +#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 index bb4c268..097c86a 100644 --- a/gl_utils.h +++ b/gl_utils.h @@ -1,34 +1,13 @@ +#pragma once + #include +#include #define check_gl() __check_gl(__FILE__, __LINE__) +void __check_gl(const char *file, int line); -static void __check_gl(const char *file, int line) -{ - GLenum error = glGetError(); +extern const GLfloat gl_quad_vertices[8]; +extern const GLfloat gl_quad_texcoords[8]; - 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); -} +GLuint gl_load_shader(const char *path, GLenum type); +GLuint gl_link_program(GLuint *shaders, size_t num_shaders); diff --git a/main.c b/main.c index dd825fe..3e38311 100644 --- a/main.c +++ b/main.c @@ -18,11 +18,20 @@ #include #include #include +#include "gl_utils.h" #include "camera_config.h" #include "quickpreview.h" #include "io_pipeline.h" #include "process_pipeline.h" +#define RENDERDOC + +#ifdef RENDERDOC +#include +#include +RENDERDOC_API_1_1_2 *rdoc_api = NULL; +#endif + enum user_control { USER_CONTROL_ISO, USER_CONTROL_SHUTTER }; static bool camera_is_initialized = false; @@ -42,7 +51,8 @@ static int exposure; static bool has_auto_focus_continuous; static bool has_auto_focus_start; -static cairo_surface_t *surface = NULL; +static MPProcessPipelineBuffer *current_preview_buffer = NULL; + static cairo_surface_t *status_surface = NULL; static char last_path[260] = ""; @@ -157,21 +167,21 @@ void mp_main_set_zbar_result(MPZBarScanResult *result) } static bool -set_preview(cairo_surface_t *image) +set_preview(MPProcessPipelineBuffer *buffer) { - if (surface) { - cairo_surface_destroy(surface); + if (current_preview_buffer) { + mp_process_pipeline_buffer_unref(current_preview_buffer); } - surface = image; + current_preview_buffer = buffer; gtk_widget_queue_draw(preview); return false; } void -mp_main_set_preview(cairo_surface_t *image) +mp_main_set_preview(MPProcessPipelineBuffer *buffer) { g_main_context_invoke_full(g_main_context_default(), G_PRIORITY_DEFAULT_IDLE, - (GSourceFunc)set_preview, image, NULL); + (GSourceFunc)set_preview, buffer, NULL); } static void transform_centered(cairo_t *cr, uint32_t dst_width, uint32_t dst_height, @@ -210,7 +220,7 @@ capture_completed(struct capture_completed_args *args) { strncpy(last_path, args->fname, 259); - gtk_image_set_from_surface(GTK_IMAGE(thumb_last), args->thumb); + // gtk_image_set_from_surface(GTK_IMAGE(thumb_last), args->thumb); gtk_spinner_stop(GTK_SPINNER(process_spinner)); gtk_stack_set_visible_child(GTK_STACK(open_last_stack), thumb_last); @@ -234,7 +244,7 @@ mp_main_capture_completed(cairo_surface_t *thumb, const char *fname) static void draw_controls() { - cairo_t *cr; + // cairo_t *cr; char iso[6]; int temp; char shutterangle[6]; @@ -259,72 +269,124 @@ draw_controls() cairo_surface_destroy(status_surface); // Make a service to show status of controls, 32px high - if (gtk_widget_get_window(preview) == NULL) { + // if (gtk_widget_get_window(preview) == NULL) { + // return; + // } + // status_surface = + // gdk_window_create_similar_surface(gtk_widget_get_window(preview), + // CAIRO_CONTENT_COLOR_ALPHA, + // preview_width, 32); + + // cr = cairo_create(status_surface); + // cairo_set_source_rgba(cr, 0, 0, 0, 0.0); + // cairo_paint(cr); + + // // Draw the outlines for the headings + // cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, + // CAIRO_FONT_WEIGHT_BOLD); + // cairo_set_font_size(cr, 9); + // cairo_set_source_rgba(cr, 0, 0, 0, 1); + + // cairo_move_to(cr, 16, 16); + // cairo_text_path(cr, "ISO"); + // cairo_stroke(cr); + + // cairo_move_to(cr, 60, 16); + // cairo_text_path(cr, "Shutter"); + // cairo_stroke(cr); + + // // Draw the fill for the headings + // cairo_set_source_rgba(cr, 1, 1, 1, 1); + // cairo_move_to(cr, 16, 16); + // cairo_show_text(cr, "ISO"); + // cairo_move_to(cr, 60, 16); + // cairo_show_text(cr, "Shutter"); + + // // Draw the outlines for the values + // cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, + // CAIRO_FONT_WEIGHT_NORMAL); + // cairo_set_font_size(cr, 11); + // cairo_set_source_rgba(cr, 0, 0, 0, 1); + + // cairo_move_to(cr, 16, 26); + // cairo_text_path(cr, iso); + // cairo_stroke(cr); + + // cairo_move_to(cr, 60, 26); + // cairo_text_path(cr, shutterangle); + // cairo_stroke(cr); + + // // Draw the fill for the values + // cairo_set_source_rgba(cr, 1, 1, 1, 1); + // cairo_move_to(cr, 16, 26); + // cairo_show_text(cr, iso); + // cairo_move_to(cr, 60, 26); + // cairo_show_text(cr, shutterangle); + + // cairo_destroy(cr); + + // gtk_widget_queue_draw_area(preview, 0, 0, preview_width, 32); +} + +static GLuint blit_program; +static GLuint blit_uniform_texture; + +static void +preview_realize(GtkGLArea *area) +{ + gtk_gl_area_make_current(area); + + if (gtk_gl_area_get_error(area) != NULL) { return; } - status_surface = - gdk_window_create_similar_surface(gtk_widget_get_window(preview), - CAIRO_CONTENT_COLOR_ALPHA, - preview_width, 32); - cr = cairo_create(status_surface); - cairo_set_source_rgba(cr, 0, 0, 0, 0.0); - cairo_paint(cr); + GLuint blit_shaders[] = { + gl_load_shader("data/blit.vert", GL_VERTEX_SHADER), + gl_load_shader("data/blit.frag", GL_FRAGMENT_SHADER), + }; - // Draw the outlines for the headings - cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, - CAIRO_FONT_WEIGHT_BOLD); - cairo_set_font_size(cr, 9); - cairo_set_source_rgba(cr, 0, 0, 0, 1); + blit_program = gl_link_program(blit_shaders, 2); + glBindAttribLocation(blit_program, 0, "vert"); + glBindAttribLocation(blit_program, 1, "tex_coord"); + check_gl(); - cairo_move_to(cr, 16, 16); - cairo_text_path(cr, "ISO"); - cairo_stroke(cr); - - cairo_move_to(cr, 60, 16); - cairo_text_path(cr, "Shutter"); - cairo_stroke(cr); - - // Draw the fill for the headings - cairo_set_source_rgba(cr, 1, 1, 1, 1); - cairo_move_to(cr, 16, 16); - cairo_show_text(cr, "ISO"); - cairo_move_to(cr, 60, 16); - cairo_show_text(cr, "Shutter"); - - // Draw the outlines for the values - cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, - CAIRO_FONT_WEIGHT_NORMAL); - cairo_set_font_size(cr, 11); - cairo_set_source_rgba(cr, 0, 0, 0, 1); - - cairo_move_to(cr, 16, 26); - cairo_text_path(cr, iso); - cairo_stroke(cr); - - cairo_move_to(cr, 60, 26); - cairo_text_path(cr, shutterangle); - cairo_stroke(cr); - - // Draw the fill for the values - cairo_set_source_rgba(cr, 1, 1, 1, 1); - cairo_move_to(cr, 16, 26); - cairo_show_text(cr, iso); - cairo_move_to(cr, 60, 26); - cairo_show_text(cr, shutterangle); - - cairo_destroy(cr); - - gtk_widget_queue_draw_area(preview, 0, 0, preview_width, 32); + blit_uniform_texture = glGetUniformLocation(blit_program, "texture"); } static gboolean -preview_draw(GtkWidget *widget, cairo_t *cr, gpointer data) +preview_draw(GtkGLArea *area, GdkGLContext *ctx, gpointer data) { + if (gtk_gl_area_get_error(area) != NULL) { + return FALSE; + } + if (!camera_is_initialized) { return FALSE; } +// #ifdef RENDERDOC +// if (rdoc_api) rdoc_api->StartFrameCapture(NULL, NULL); +// #endif + + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + + if (current_preview_buffer) { + glUseProgram(blit_program); + + 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(); + } + + /* // Clear preview area with black cairo_paint(cr); @@ -367,19 +429,23 @@ preview_draw(GtkWidget *widget, cairo_t *cr, gpointer data) // Draw control overlay cairo_set_source_surface(cr, status_surface, 0, 0); cairo_paint(cr); + */ + + glFlush(); + +// #ifdef RENDERDOC +// if(rdoc_api) rdoc_api->EndFrameCapture(NULL, NULL); +// #endif + return FALSE; } static gboolean -preview_configure(GtkWidget *widget, GdkEventConfigure *event) +preview_resize(GtkWidget *widget, int width, int height, gpointer data) { - int new_preview_width = gtk_widget_get_allocated_width(widget); - int new_preview_height = gtk_widget_get_allocated_height(widget); - - if (preview_width != new_preview_width || - preview_height != new_preview_height) { - preview_width = new_preview_width; - preview_height = new_preview_height; + if (preview_width != width || preview_height != height) { + preview_width = width; + preview_height = height; update_io_pipeline(); } @@ -422,158 +488,158 @@ on_shutter_clicked(GtkWidget *widget, gpointer user_data) mp_io_pipeline_capture(); } -void -on_capture_shortcut(void) -{ - on_shutter_clicked(NULL, NULL); -} +// void +// on_capture_shortcut(void) +// { +// on_shutter_clicked(NULL, NULL); +// } -static bool -check_point_inside_bounds(int x, int y, int *bounds_x, int *bounds_y) -{ - bool right = false, left = false, top = false, bottom = false; +// static bool +// check_point_inside_bounds(int x, int y, int *bounds_x, int *bounds_y) +// { +// bool right = false, left = false, top = false, bottom = false; - for (int i = 0; i < 4; ++i) { - if (x <= bounds_x[i]) - left = true; - if (x >= bounds_x[i]) - right = true; - if (y <= bounds_y[i]) - top = true; - if (y >= bounds_y[i]) - bottom = true; - } +// for (int i = 0; i < 4; ++i) { +// if (x <= bounds_x[i]) +// left = true; +// if (x >= bounds_x[i]) +// right = true; +// if (y <= bounds_y[i]) +// top = true; +// if (y >= bounds_y[i]) +// bottom = true; +// } - return right && left && top && bottom; -} +// return right && left && top && bottom; +// } -static void -on_zbar_code_tapped(GtkWidget *widget, const MPZBarCode *code) -{ - GtkWidget *dialog; - GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT; - bool data_is_url = g_uri_is_valid( - code->data, G_URI_FLAGS_PARSE_RELAXED, NULL); +// static void +// on_zbar_code_tapped(GtkWidget *widget, const MPZBarCode *code) +// { +// GtkWidget *dialog; +// GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT; +// bool data_is_url = g_uri_is_valid( +// code->data, G_URI_FLAGS_PARSE_RELAXED, NULL); - char* data = strdup(code->data); +// char* data = strdup(code->data); - if (data_is_url) { - dialog = gtk_message_dialog_new( - GTK_WINDOW(gtk_widget_get_toplevel(widget)), - flags, - GTK_MESSAGE_QUESTION, - GTK_BUTTONS_NONE, - "Found a URL '%s' encoded in a %s code.", - code->data, - code->type); - gtk_dialog_add_buttons( - GTK_DIALOG(dialog), - "_Open URL", - GTK_RESPONSE_YES, - NULL); - } else { - dialog = gtk_message_dialog_new( - GTK_WINDOW(gtk_widget_get_toplevel(widget)), - flags, - GTK_MESSAGE_QUESTION, - GTK_BUTTONS_NONE, - "Found '%s' encoded in a %s code.", - code->data, - code->type); - } - gtk_dialog_add_buttons( - GTK_DIALOG(dialog), - "_Copy", - GTK_RESPONSE_ACCEPT, - "_Cancel", - GTK_RESPONSE_CANCEL, - NULL); +// if (data_is_url) { +// dialog = gtk_message_dialog_new( +// GTK_WINDOW(gtk_widget_get_toplevel(widget)), +// flags, +// GTK_MESSAGE_QUESTION, +// GTK_BUTTONS_NONE, +// "Found a URL '%s' encoded in a %s code.", +// code->data, +// code->type); +// gtk_dialog_add_buttons( +// GTK_DIALOG(dialog), +// "_Open URL", +// GTK_RESPONSE_YES, +// NULL); +// } else { +// dialog = gtk_message_dialog_new( +// GTK_WINDOW(gtk_widget_get_toplevel(widget)), +// flags, +// GTK_MESSAGE_QUESTION, +// GTK_BUTTONS_NONE, +// "Found '%s' encoded in a %s code.", +// code->data, +// code->type); +// } +// gtk_dialog_add_buttons( +// GTK_DIALOG(dialog), +// "_Copy", +// GTK_RESPONSE_ACCEPT, +// "_Cancel", +// GTK_RESPONSE_CANCEL, +// NULL); - int result = gtk_dialog_run(GTK_DIALOG(dialog)); +// int result = gtk_dialog_run(GTK_DIALOG(dialog)); - GError *error = NULL; - switch (result) { - case GTK_RESPONSE_YES: - if (!g_app_info_launch_default_for_uri(data, - NULL, &error)) { - g_printerr("Could not launch application: %s\n", - error->message); - } - case GTK_RESPONSE_ACCEPT: - gtk_clipboard_set_text( - gtk_clipboard_get(GDK_SELECTION_PRIMARY), - data, -1); - case GTK_RESPONSE_CANCEL: - break; - default: - g_printerr("Wrong dialog result: %d\n", result); - } - gtk_widget_destroy(dialog); -} +// GError *error = NULL; +// switch (result) { +// case GTK_RESPONSE_YES: +// if (!g_app_info_launch_default_for_uri(data, +// NULL, &error)) { +// g_printerr("Could not launch application: %s\n", +// error->message); +// } +// case GTK_RESPONSE_ACCEPT: +// gtk_clipboard_set_text( +// gtk_clipboard_get(GDK_SELECTION_PRIMARY), +// data, -1); +// case GTK_RESPONSE_CANCEL: +// break; +// default: +// g_printerr("Wrong dialog result: %d\n", result); +// } +// gtk_widget_destroy(dialog); +// } -void -on_preview_tap(GtkWidget *widget, GdkEventButton *event, gpointer user_data) -{ - if (event->type != GDK_BUTTON_PRESS) - return; +// void +// on_preview_tap(GtkWidget *widget, GdkEventButton *event, gpointer user_data) +// { +// if (event->type != GDK_BUTTON_PRESS) +// return; - // Handle taps on the controls - if (event->y < 32) { - if (gtk_widget_is_visible(control_box)) { - gtk_widget_hide(control_box); - return; - } else { - gtk_widget_show(control_box); - } +// // Handle taps on the controls +// if (event->y < 32) { +// if (gtk_widget_is_visible(control_box)) { +// gtk_widget_hide(control_box); +// return; +// } else { +// gtk_widget_show(control_box); +// } - if (event->x < 60) { - // ISO - current_control = USER_CONTROL_ISO; - gtk_label_set_text(GTK_LABEL(control_name), "ISO"); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto), - !gain_is_manual); - gtk_adjustment_set_lower(control_slider, 0.0); - gtk_adjustment_set_upper(control_slider, (float)gain_max); - gtk_adjustment_set_value(control_slider, (double)gain); +// if (event->x < 60) { +// // ISO +// current_control = USER_CONTROL_ISO; +// gtk_label_set_text(GTK_LABEL(control_name), "ISO"); +// gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto), +// !gain_is_manual); +// gtk_adjustment_set_lower(control_slider, 0.0); +// gtk_adjustment_set_upper(control_slider, (float)gain_max); +// gtk_adjustment_set_value(control_slider, (double)gain); - } else if (event->x > 60 && event->x < 120) { - // Shutter angle - current_control = USER_CONTROL_SHUTTER; - gtk_label_set_text(GTK_LABEL(control_name), "Shutter"); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto), - !exposure_is_manual); - gtk_adjustment_set_lower(control_slider, 1.0); - gtk_adjustment_set_upper(control_slider, 360.0); - gtk_adjustment_set_value(control_slider, (double)exposure); - } +// } else if (event->x > 60 && event->x < 120) { +// // Shutter angle +// current_control = USER_CONTROL_SHUTTER; +// gtk_label_set_text(GTK_LABEL(control_name), "Shutter"); +// gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto), +// !exposure_is_manual); +// gtk_adjustment_set_lower(control_slider, 1.0); +// gtk_adjustment_set_upper(control_slider, 360.0); +// gtk_adjustment_set_value(control_slider, (double)exposure); +// } - return; - } +// return; +// } - // Tapped zbar result - if (zbar_result) { - // Transform the event coordinates to the image - int width = cairo_image_surface_get_width(surface); - int height = cairo_image_surface_get_height(surface); - double scale = MIN(preview_width / (double)width, preview_height / (double)height); - int x = (event->x - preview_width / 2) / scale + width / 2; - int y = (event->y - preview_height / 2) / scale + height / 2; +// // Tapped zbar result +// if (zbar_result) { +// // Transform the event coordinates to the image +// int width = cairo_image_surface_get_width(surface); +// int height = cairo_image_surface_get_height(surface); +// double scale = MIN(preview_width / (double)width, preview_height / (double)height); +// int x = (event->x - preview_width / 2) / scale + width / 2; +// int y = (event->y - preview_height / 2) / scale + height / 2; - for (uint8_t i = 0; i < zbar_result->size; ++i) { - MPZBarCode *code = &zbar_result->codes[i]; +// for (uint8_t i = 0; i < zbar_result->size; ++i) { +// MPZBarCode *code = &zbar_result->codes[i]; - if (check_point_inside_bounds(x, y, code->bounds_x, code->bounds_y)) { - on_zbar_code_tapped(widget, code); - return; - } - } - } +// if (check_point_inside_bounds(x, y, code->bounds_x, code->bounds_y)) { +// on_zbar_code_tapped(widget, code); +// return; +// } +// } +// } - // Tapped preview image itself, try focussing - if (has_auto_focus_start) { - mp_io_pipeline_focus(); - } -} +// // Tapped preview image itself, try focussing +// if (has_auto_focus_start) { +// mp_io_pipeline_focus(); +// } +// } void on_error_close_clicked(GtkWidget *widget, gpointer user_data) @@ -690,22 +756,35 @@ on_control_slider_changed(GtkAdjustment *widget, gpointer user_data) static void on_realize(GtkWidget *window, gpointer *data) { - mp_process_pipeline_init_gl(gtk_widget_get_window(window)); + GtkNative *native = gtk_widget_get_native(window); + mp_process_pipeline_init_gl(gtk_native_get_surface(native)); } -int -main(int argc, char *argv[]) +typedef struct { - if (!mp_load_config()) - return 1; + GtkApplication parent_instance; +} MegapixelsApp; - setenv("LC_NUMERIC", "C", 1); +typedef GtkApplicationClass MegapixelsAppClass; + +GType megapixels_app_get_type (void); +G_DEFINE_TYPE(MegapixelsApp, megapixels_app, GTK_TYPE_APPLICATION) + +static void +startup(GApplication *app) +{ + G_APPLICATION_CLASS(megapixels_app_parent_class)->startup(app); - gtk_init(&argc, &argv); g_object_set(gtk_settings_get_default(), "gtk-application-prefer-dark-theme", TRUE, NULL); - GtkBuilder *builder = gtk_builder_new_from_resource( - "/org/postmarketos/Megapixels/camera.glade"); + + 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"); + } GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window")); GtkWidget *shutter = GTK_WIDGET(gtk_builder_get_object(builder, "shutter")); @@ -734,7 +813,7 @@ main(int argc, char *argv[]) GTK_ADJUSTMENT(gtk_builder_get_object(builder, "control_adj")); control_auto = GTK_WIDGET(gtk_builder_get_object(builder, "control_auto")); g_signal_connect(window, "realize", G_CALLBACK(on_realize), NULL); - g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); + // g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(shutter, "clicked", G_CALLBACK(on_shutter_clicked), NULL); g_signal_connect(error_close, "clicked", G_CALLBACK(on_error_close_clicked), NULL); @@ -748,14 +827,15 @@ main(int argc, char *argv[]) NULL); g_signal_connect(open_directory, "clicked", G_CALLBACK(on_open_directory_clicked), NULL); - g_signal_connect(preview, "draw", G_CALLBACK(preview_draw), NULL); - g_signal_connect(preview, "configure-event", G_CALLBACK(preview_configure), - NULL); - gtk_widget_set_events(preview, gtk_widget_get_events(preview) | - GDK_BUTTON_PRESS_MASK | - GDK_POINTER_MOTION_MASK); - g_signal_connect(preview, "button-press-event", G_CALLBACK(on_preview_tap), + g_signal_connect(preview, "realize", G_CALLBACK(preview_realize), NULL); + g_signal_connect(preview, "render", G_CALLBACK(preview_draw), NULL); + g_signal_connect(preview, "resize", G_CALLBACK(preview_resize), NULL); + // gtk_widget_set_events(preview, gtk_widget_get_events(preview) | + // GDK_BUTTON_PRESS_MASK | + // GDK_POINTER_MOTION_MASK); + // g_signal_connect(preview, "button-press-event", G_CALLBACK(on_preview_tap), + // NULL); g_signal_connect(control_auto, "toggled", G_CALLBACK(on_control_auto_toggled), NULL); g_signal_connect(control_slider, "value-changed", @@ -763,7 +843,7 @@ main(int argc, char *argv[]) GtkCssProvider *provider = gtk_css_provider_new(); if (access("camera.css", F_OK) != -1) { - gtk_css_provider_load_from_path(provider, "camera.css", NULL); + gtk_css_provider_load_from_path(provider, "camera.css"); } else { gtk_css_provider_load_from_resource( provider, "/org/postmarketos/Megapixels/camera.css"); @@ -775,26 +855,81 @@ main(int argc, char *argv[]) gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_USER); - GClosure* capture_shortcut = g_cclosure_new(on_capture_shortcut, 0, 0); + // GClosure* capture_shortcut = g_cclosure_new(on_capture_shortcut, 0, 0); - GtkAccelGroup* accel_group = gtk_accel_group_new(); - gtk_accel_group_connect(accel_group, - GDK_KEY_space, - 0, - 0, - capture_shortcut); + // GtkAccelGroup* accel_group = gtk_accel_group_new(); + // gtk_accel_group_connect(accel_group, + // GDK_KEY_space, + // 0, + // 0, + // capture_shortcut); - gtk_window_add_accel_group(GTK_WINDOW(window), accel_group); + // gtk_window_add_accel_group(GTK_WINDOW(window), accel_group); 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); - gtk_main(); +} +static void +shutdown(GApplication *app) +{ + // Only do cleanup in development, let the OS clean up otherwise +#ifdef DEBUG mp_io_pipeline_stop(); +#endif + + G_APPLICATION_CLASS(megapixels_app_parent_class)->shutdown(app); +} + +static void +megapixels_app_init(MegapixelsApp *app) +{ +} + +static void +megapixels_app_class_init(MegapixelsAppClass *class) +{ + GApplicationClass *application_class = G_APPLICATION_CLASS(class); + + application_class->startup = startup; + application_class->shutdown = shutdown; +} + +int +main(int argc, char *argv[]) +{ +#ifdef RENDERDOC + { + void *mod = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD); + if (mod) + { + pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)dlsym(mod, "RENDERDOC_GetAPI"); + int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2, (void **)&rdoc_api); + assert(ret == 1); + } + else + { + printf("Renderdoc not found\n"); + } + } +#endif + + if (!mp_load_config()) + return 1; + + setenv("LC_NUMERIC", "C", 1); + + MegapixelsApp *app = g_object_new( + megapixels_app_get_type(), + "application-id", "org.postmarketos.Megapixels", + NULL); + + g_application_run(G_APPLICATION(app), argc, argv); return 0; } diff --git a/main.h b/main.h index 2995993..fe899ac 100644 --- a/main.h +++ b/main.h @@ -2,6 +2,7 @@ #include "camera_config.h" #include "zbar_pipeline.h" +#include "process_pipeline.h" #include "gtk/gtk.h" #define MP_MAIN_THUMB_SIZE 24 @@ -23,7 +24,7 @@ struct mp_main_state { void mp_main_update_state(const struct mp_main_state *state); -void mp_main_set_preview(cairo_surface_t *image); +void mp_main_set_preview(MPProcessPipelineBuffer *buffer); void mp_main_capture_completed(cairo_surface_t *thumb, const char *fname); void mp_main_set_zbar_result(MPZBarScanResult *result); diff --git a/meson.build b/meson.build index 40f31c6..ef331a6 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('megapixels', 'c') gnome = import('gnome') -gtkdep = dependency('gtk+-3.0') +gtkdep = dependency('gtk4') tiff = dependency('libtiff-4') zbar = dependency('zbar') threads = dependency('threads') @@ -34,6 +34,7 @@ executable('megapixels', 'ini.c', 'quickpreview.c', 'gl_quickpreview.c', + 'gl_utils.c', 'camera.c', 'device.c', 'pipeline.c', diff --git a/process_pipeline.c b/process_pipeline.c index 360aa3c..8e178ff 100644 --- a/process_pipeline.c +++ b/process_pipeline.c @@ -16,7 +16,7 @@ #include "gl_utils.h" #include #include -#include +// #include #include #include @@ -151,130 +151,100 @@ mp_process_pipeline_sync() #define NUM_BUFFERS 4 +struct _MPProcessPipelineBuffer { + GLuint texture_id; + + _Atomic(int) refcount; +}; +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) +{ + ++buf->refcount; +} + +void +mp_process_pipeline_buffer_unref(MPProcessPipelineBuffer *buf) +{ + --buf->refcount; +} + +uint32_t +mp_process_pipeline_buffer_get_texture_id(MPProcessPipelineBuffer *buf) +{ + return buf->texture_id; +} + static GLQuickPreview *gl_quick_preview_state = NULL; -static EGLDisplay egl_display = EGL_NO_DISPLAY; -static EGLContext egl_context = EGL_NO_CONTEXT; - -// struct buffer { -// GLuint texture_id; -// EGLImage egl_image; -// int dma_fd; -// int dma_stride; -// int dma_offset; -// void *data; -// }; -// static struct buffer input_buffers[NUM_BUFFERS]; -// static struct buffer output_buffers[NUM_BUFFERS]; +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"; -} +// 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 -#include #include -RENDERDOC_API_1_1_2 *rdoc_api = NULL; +extern RENDERDOC_API_1_1_2 *rdoc_api; #endif static void -init_gl(MPPipeline *pipeline, GdkWindow **window) +init_gl(MPPipeline *pipeline, GdkSurface **surface) { -#ifdef RENDERDOC - { - void *mod = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD); - if (mod) - { - pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)dlsym(mod, "RENDERDOC_GetAPI"); - int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2, (void **)&rdoc_api); - assert(ret == 1); - } - else - { - printf("Renderdoc not found\n"); - } + GError *error = NULL; + context = gdk_surface_create_gl_context(*surface, &error); + if (context == NULL) { + printf("Failed to initialize OpenGL context: %s\n", error->message); + g_clear_error(&error); + return; } + + gdk_gl_context_set_use_es(context, true); + gdk_gl_context_set_required_version(context, 2, 0); +#ifdef DEBUG + gdk_gl_context_set_debug_enabled(context, true); +#else + gdk_gl_context_set_debug_enabled(context, false); #endif - GdkDisplay *gdk_display = gdk_window_get_display(*window); - egl_display = eglGetDisplay((EGLNativeDisplayType) gdk_wayland_display_get_wl_display(gdk_display)); - assert(egl_display != EGL_NO_DISPLAY); - - EGLint major, minor; - if (!eglInitialize(egl_display, &major, &minor)) { - printf("Failed to initialize egl: %s\n", egl_get_error_str()); - return; - } - - if (!eglBindAPI(EGL_OPENGL_ES_API)) { - printf("Failed to bind OpenGL ES: %s\n", egl_get_error_str()); - return; - } - - printf("extensions: %s\n", eglQueryString(egl_display, EGL_EXTENSIONS)); - - EGLint config_attrs[1] = { - EGL_NONE - }; - - EGLConfig config; - EGLint num_configs = 0; - if (!eglChooseConfig(egl_display, config_attrs, &config, 1, &num_configs)) { - printf("Failed to pick a egl config: %s\n", egl_get_error_str()); - return; - } - - if (num_configs != 1) { - printf("No egl configs found: %s\n", egl_get_error_str()); - return; - } - - EGLint context_attrs[5] = { - EGL_CONTEXT_CLIENT_VERSION, - 2, - EGL_CONTEXT_FLAGS_KHR, - EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, - EGL_NONE, - }; - - egl_context = eglCreateContext(egl_display, config, NULL, context_attrs); - - if (egl_context == EGL_NO_CONTEXT) { - printf("Failed to create OpenGL ES context: %s\n", egl_get_error_str()); - return; - } - - if (!eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_context)) - { - printf("Failed to set current OpenGL context: %s\n", egl_get_error_str()); + gdk_gl_context_realize(context, &error); + if (error != NULL) { + printf("Failed to create OpenGL context: %s\n", error->message); + g_clear_object(&context); + g_clear_error(&error); return; } + gdk_gl_context_make_current(context); check_gl(); eglExportDMABUFImageMESA = (PFNEGLEXPORTDMABUFIMAGEMESAPROC) @@ -300,153 +270,154 @@ init_gl(MPPipeline *pipeline, GdkWindow **window) gl_quick_preview_state = gl_quick_preview_new(); check_gl(); + 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); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + + glBindTexture(GL_TEXTURE_2D, 0); + printf("Initialized OpenGL\n"); } void -mp_process_pipeline_init_gl(GdkWindow *window) +mp_process_pipeline_init_gl(GdkSurface *surface) { - mp_pipeline_invoke(pipeline, (MPPipelineCallback) init_gl, &window, sizeof(GdkWindow *)); + mp_pipeline_invoke(pipeline, (MPPipelineCallback) init_gl, &surface, sizeof(GdkSurface *)); } static cairo_surface_t * process_image_for_preview(const uint8_t *image) { - cairo_surface_t *surface; - clock_t t1 = clock(); - if (gl_quick_preview_state && ql_quick_preview_supports_format(gl_quick_preview_state, mode.pixel_format)) { -#ifdef RENDERDOC - if (rdoc_api) rdoc_api->StartFrameCapture(NULL, NULL); -#endif - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - check_gl(); + assert(gl_quick_preview_state && ql_quick_preview_supports_format(gl_quick_preview_state, mode.pixel_format)); - GLuint textures[2]; - glGenTextures(2, textures); - - glBindTexture(GL_TEXTURE_2D, textures[0]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, mode.width, mode.height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image); - check_gl(); - - glBindTexture(GL_TEXTURE_2D, textures[1]); - 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, preview_width, preview_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - check_gl(); - - gl_quick_preview( - gl_quick_preview_state, - textures[1], preview_width, preview_height, - textures[0], mode.width, mode.height, - mode.pixel_format, - camera->rotate, camera->mirrored, - camera->previewmatrix[0] == 0 ? NULL : camera->previewmatrix, - camera->blacklevel); - 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(); - - clock_t t2 = clock(); - printf("%fms\n", (float)(t2 - t1) / CLOCKS_PER_SEC * 1000); - - // { - // glBindTexture(GL_TEXTURE_2D, textures[1]); - // EGLImage egl_image = eglCreateImage(egl_display, egl_context, EGL_GL_TEXTURE_2D, (EGLClientBuffer)(size_t)textures[1], NULL); - - // // Make sure it's in the expected format - // int fourcc; - // eglExportDMABUFImageQueryMESA(egl_display, egl_image, &fourcc, NULL, NULL); - // assert(fourcc == DRM_FORMAT_ABGR8888); - - - // int dmabuf_fd; - // int stride, offset; - // eglExportDMABUFImageMESA(egl_display, egl_image, &dmabuf_fd, &stride, &offset); - - // int fsize = lseek(dmabuf_fd, 0, SEEK_END); - // printf("SIZE %d STRIDE %d OFFSET %d SIZE %d:%d\n", fsize, stride, offset, preview_width, preview_height); - - // size_t size = stride * preview_height; - // uint32_t *data = mmap(NULL, fsize, PROT_READ, MAP_SHARED, dmabuf_fd, 0); - // assert(data != MAP_FAILED); - - // int pixel_stride = stride / 4; - - // for (size_t y = 0; y < preview_height; ++y) { - // for (size_t x = 0; x < preview_width; ++x) { - // uint32_t p = data[x + y * pixel_stride]; - // pixels[x + y * preview_width] = p; - // // uint16_t p = data[x + y * stride]; - // // uint32_t r = (p & 0b11111); - // // uint32_t g = ((p >> 5) & 0b11111); - // // uint32_t b = ((p >> 10) & 0b11111); - // // pixels[x + y * preview_width] = (r << 16) | (g << 8) | b; - // } - // // memcpy(pixels + preview_width * y, data + stride * y, preview_width * sizeof(uint32_t)); - // } - - // { - // FILE *f = fopen("test.raw", "w"); - // fwrite(data, fsize, 1, f); - // fclose(f); - // } - - // // memcpy(pixels, data, size); - // munmap(data, size); - // close(dmabuf_fd); - // } - glReadPixels(0, 0, preview_width, preview_height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - check_gl(); - - glBindTexture(GL_TEXTURE_2D, 0); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - -#ifdef RENDERDOC - if(rdoc_api) rdoc_api->EndFrameCapture(NULL, NULL); -#endif - } else { - uint32_t surface_width, surface_height, skip; - quick_preview_size(&surface_width, &surface_height, &skip, preview_width, - preview_height, mode.width, mode.height, - mode.pixel_format, camera->rotate); - - surface = cairo_image_surface_create( - CAIRO_FORMAT_RGB24, surface_width, surface_height); - - uint8_t *pixels = cairo_image_surface_get_data(surface); - - quick_preview((uint32_t *)pixels, surface_width, surface_height, image, - mode.width, mode.height, mode.pixel_format, - camera->rotate, camera->mirrored, - camera->previewmatrix[0] == 0 ? NULL : camera->previewmatrix, - camera->blacklevel, skip); + // Pick an available buffer + MPProcessPipelineBuffer *output_buffer = NULL; + for (size_t i = 0; i < NUM_BUFFERS; ++i) { + if (output_buffers[i].refcount == 0) { + output_buffer = &output_buffers[i]; + } } + if (output_buffer == NULL) { + return NULL; + } + assert(output_buffer != NULL); + +#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; + glGenTextures(1, &input_texture); + glBindTexture(GL_TEXTURE_2D, input_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + 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); + 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); + + clock_t t2 = clock(); + printf("%fms\n", (float)(t2 - t1) / CLOCKS_PER_SEC * 1000); + + // { + // glBindTexture(GL_TEXTURE_2D, textures[1]); + // EGLImage egl_image = eglCreateImage(egl_display, egl_context, EGL_GL_TEXTURE_2D, (EGLClientBuffer)(size_t)textures[1], NULL); + + // // Make sure it's in the expected format + // int fourcc; + // eglExportDMABUFImageQueryMESA(egl_display, egl_image, &fourcc, NULL, NULL); + // assert(fourcc == DRM_FORMAT_ABGR8888); + + + // int dmabuf_fd; + // int stride, offset; + // eglExportDMABUFImageMESA(egl_display, egl_image, &dmabuf_fd, &stride, &offset); + + // int fsize = lseek(dmabuf_fd, 0, SEEK_END); + // printf("SIZE %d STRIDE %d OFFSET %d SIZE %d:%d\n", fsize, stride, offset, preview_width, preview_height); + + // size_t size = stride * preview_height; + // uint32_t *data = mmap(NULL, fsize, PROT_READ, MAP_SHARED, dmabuf_fd, 0); + // assert(data != MAP_FAILED); + + // int pixel_stride = stride / 4; + + // for (size_t y = 0; y < preview_height; ++y) { + // for (size_t x = 0; x < preview_width; ++x) { + // uint32_t p = data[x + y * pixel_stride]; + // pixels[x + y * preview_width] = p; + // // uint16_t p = data[x + y * stride]; + // // uint32_t r = (p & 0b11111); + // // uint32_t g = ((p >> 5) & 0b11111); + // // uint32_t b = ((p >> 10) & 0b11111); + // // pixels[x + y * preview_width] = (r << 16) | (g << 8) | b; + // } + // // memcpy(pixels + preview_width * y, data + stride * y, preview_width * sizeof(uint32_t)); + // } + + // { + // FILE *f = fopen("test.raw", "w"); + // fwrite(data, fsize, 1, f); + // fclose(f); + // } + + // // memcpy(pixels, data, size); + // munmap(data, size); + // close(dmabuf_fd); + // } + // glReadPixels(0, 0, preview_width, preview_height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + // check_gl(); + +#ifdef RENDERDOC + if(rdoc_api) rdoc_api->EndFrameCapture(NULL, NULL); +#endif + + mp_process_pipeline_buffer_ref(output_buffer); + mp_main_set_preview(output_buffer); + // Create a thumbnail from the preview for the last capture cairo_surface_t *thumb = NULL; - if (captures_remaining == 1) { - printf("Making thumbnail\n"); - thumb = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, MP_MAIN_THUMB_SIZE, MP_MAIN_THUMB_SIZE); + // if (captures_remaining == 1) { + // printf("Making thumbnail\n"); + // thumb = cairo_image_surface_create( + // CAIRO_FORMAT_ARGB32, MP_MAIN_THUMB_SIZE, MP_MAIN_THUMB_SIZE); - cairo_t *cr = cairo_create(thumb); - draw_surface_scaled_centered( - cr, MP_MAIN_THUMB_SIZE, MP_MAIN_THUMB_SIZE, surface); - cairo_destroy(cr); - } + // cairo_t *cr = cairo_create(thumb); + // draw_surface_scaled_centered( + // cr, MP_MAIN_THUMB_SIZE, MP_MAIN_THUMB_SIZE, surface); + // cairo_destroy(cr); + // } // Pass processed preview to main and zbar - mp_zbar_pipeline_process_image(cairo_surface_reference(surface)); - mp_main_set_preview(surface); + // mp_zbar_pipeline_process_image(cairo_surface_reference(surface)); return thumb; } @@ -755,17 +726,39 @@ mp_process_pipeline_capture() mp_pipeline_invoke(pipeline, capture, NULL, 0); } +static void +update_output_buffers() +{ + output_buffer_width = mode.width / 2; + output_buffer_height = mode.height / 2; + + if (camera->rotate != 0 || camera->rotate != 180) { + int tmp = output_buffer_width; + output_buffer_width = output_buffer_height; + output_buffer_height = tmp; + } + + for (size_t i = 0; i < NUM_BUFFERS; ++i) { + glBindTexture(GL_TEXTURE_2D, output_buffers[i].texture_id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, output_buffer_width, output_buffer_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + } + + glBindTexture(GL_TEXTURE_2D, 0); +} + 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); + camera = state->camera; mode = state->mode; - burst_length = state->burst_length; - preview_width = state->preview_width; preview_height = state->preview_height; + burst_length = state->burst_length; + // gain_is_manual = state->gain_is_manual; gain = state->gain; gain_max = state->gain_max; @@ -773,6 +766,10 @@ 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(); + } + struct mp_main_state main_state = { .camera = camera, .mode = mode, @@ -793,3 +790,6 @@ mp_process_pipeline_update_state(const struct mp_process_pipeline_state *new_sta mp_pipeline_invoke(pipeline, (MPPipelineCallback)update_state, new_state, sizeof(struct mp_process_pipeline_state)); } + +// FUCK YOU GTK +void pango_fc_font_get_languages() {} diff --git a/process_pipeline.h b/process_pipeline.h index 28f1b78..9d0f69b 100644 --- a/process_pipeline.h +++ b/process_pipeline.h @@ -2,7 +2,7 @@ #include "camera_config.h" -typedef struct _GdkWindow GdkWindow; +typedef struct _GdkSurface GdkSurface; struct mp_process_pipeline_state { const struct mp_camera_config *camera; @@ -28,8 +28,14 @@ void mp_process_pipeline_start(); void mp_process_pipeline_stop(); void mp_process_pipeline_sync(); -void mp_process_pipeline_init_gl(GdkWindow *window); +void mp_process_pipeline_init_gl(GdkSurface *window); void mp_process_pipeline_process_image(MPBuffer buffer); void mp_process_pipeline_capture(); void mp_process_pipeline_update_state(const struct mp_process_pipeline_state *state); + +typedef struct _MPProcessPipelineBuffer MPProcessPipelineBuffer; + +void mp_process_pipeline_buffer_ref(MPProcessPipelineBuffer *buf); +void mp_process_pipeline_buffer_unref(MPProcessPipelineBuffer *buf); +uint32_t mp_process_pipeline_buffer_get_texture_id(MPProcessPipelineBuffer *buf);