Browse Source

It works!

opengl
Benjamin Schaaf 1 year ago
parent
commit
337526e9b9
  1. 1
      camera.c
  2. 4
      camera.css
  3. 2
      camera.glade
  4. 335
      camera.ui
  5. 6
      config/pine64,pinephone-1.0.ini
  6. 6
      config/pine64,pinephone-1.1.ini
  7. 6
      config/pine64,pinephone-1.2.ini
  8. 11
      data/blit.frag
  9. 12
      data/blit.vert
  10. 49
      data/debayer.frag
  11. 29
      data/debayer.vert
  12. 198
      gl_quickpreview.c
  13. 128
      gl_utils.c
  14. 37
      gl_utils.h
  15. 635
      main.c
  16. 3
      main.h
  17. 3
      meson.build
  18. 452
      process_pipeline.c
  19. 10
      process_pipeline.h

1
camera.c

@ -607,6 +607,7 @@ mp_camera_capture_buffer(MPCamera *camera, MPBuffer *buffer)
/* fallthrough */
default:
errno_printerr("VIDIOC_DQBUF");
exit(1);
return false;
}
}

4
camera.css

@ -1,7 +1,3 @@
.black {
background: #000000;
}
.errorbox {
background: #dd0000;
color: #ffffff;

2
camera.glade

@ -27,7 +27,7 @@
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkDrawingArea" id="preview">
<object class="GtkGLArea" id="preview">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>

335
camera.ui

@ -0,0 +1,335 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<object class="GtkAdjustment" id="control_adj">
<property name="upper">100</property>
<property name="step-increment">1</property>
<property name="page-increment">10</property>
</object>
<object class="GtkWindow" id="window">
<property name="can-focus">0</property>
<property name="default-width">360</property>
<property name="default-height">640</property>
<property name="decorated">0</property>
<property name="child">
<object class="GtkStack" id="main_stack">
<property name="can-focus">0</property>
<child>
<object class="GtkStackPage">
<property name="name">main</property>
<property name="child">
<object class="GtkBox" id="page_main">
<property name="can-focus">0</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkGLArea" id="preview">
<property name="vexpand">1</property>
<property name="can-focus">0</property>
</object>
</child>
<child>
<object class="GtkBox" id="control_box">
<property name="visible">0</property>
<property name="can-focus">0</property>
<child>
<object class="GtkBox">
<property name="hexpand">1</property>
<property name="can-focus">0</property>
<property name="margin-start">10</property>
<property name="margin-end">10</property>
<property name="margin-top">10</property>
<property name="margin-bottom">10</property>
<child>
<object class="GtkLabel" id="control_name">
<property name="can-focus">0</property>
<property name="margin-end">10</property>
<property name="label" translatable="yes">ISO</property>
</object>
</child>
<child>
<object class="GtkScale" id="control_slider">
<property name="hexpand">1</property>
<property name="adjustment">control_adj</property>
<property name="round-digits">1</property>
</object>
</child>
<child>
<object class="GtkToggleButton" id="control_auto">
<property name="label" translatable="yes">Auto</property>
<property name="receives-default">1</property>
<property name="margin-start">10</property>
</object>
</child>
</object>
</child>
<style>
<class name="controlbox"/>
</style>
</object>
</child>
<child>
<object class="GtkActionBar" id="controls_box">
<property name="can-focus">0</property>
<child type="start">
<object class="GtkButton" id="settings">
<property name="receives-default">1</property>
<child>
<object class="GtkImage">
<property name="can-focus">0</property>
<property name="resource">/org/postmarketos/Megapixels/settings-symbolic.svg</property>
</object>
</child>
</object>
</child>
<child type="start">
<object class="GtkButton" id="switch_camera">
<property name="receives-default">1</property>
<child>
<object class="GtkImage">
<property name="can-focus">0</property>
<property name="resource">/org/postmarketos/Megapixels/switch-camera.svg</property>
</object>
</child>
</object>
</child>
<child type="center">
<object class="GtkButton" id="shutter">
<property name="width-request">48</property>
<property name="height-request">48</property>
<property name="receives-default">1</property>
<child>
<object class="GtkImage">
<property name="can-focus">0</property>
<property name="resource">/org/postmarketos/Megapixels/shutter-button.svg</property>
</object>
</child>
<style>
<class name="circular"/>
</style>
</object>
</child>
<child type="end">
<object class="GtkButton" id="open_directory">
<property name="receives-default">1</property>
<child>
<object class="GtkImage">
<property name="can-focus">0</property>
<property name="resource">/org/postmarketos/Megapixels/folder-symbolic.svg</property>
</object>
</child>
</object>
</child>
<child type="end">
<object class="GtkButton" id="open_last">
<property name="receives-default">1</property>
<child>
<object class="GtkStack" id="open_last_stack">
<property name="can-focus">0</property>
<child>
<object class="GtkImage" id="thumb_last">
<property name="width-request">24</property>
<property name="height-request">24</property>
<property name="can-focus">0</property>
</object>
</child>
<child>
<object class="GtkSpinner" id="process_spinner">
<property name="width-request">24</property>
<property name="height-request">24</property>
<property name="can-focus">0</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox" id="error_box">
<property name="visible">0</property>
<property name="can-focus">0</property>
<child>
<object class="GtkBox">
<property name="hexpand">1</property>
<property name="can-focus">0</property>
<property name="margin-start">10</property>
<property name="margin-end">10</property>
<property name="margin-top">10</property>
<property name="margin-bottom">10</property>
<property name="spacing">10</property>
<child>
<object class="GtkLabel" id="error_message">
<property name="hexpand">1</property>
<property name="can-focus">0</property>
<property name="halign">start</property>
<property name="label" translatable="yes">No error</property>
</object>
</child>
<child>
<object class="GtkButton" id="error_close">
<property name="label">gtk-close</property>
<property name="receives-default">1</property>
</object>
</child>
</object>
</child>
<style>
<class name="errorbox"/>
</style>
</object>
</child>
</object>
</property>
</object>
</child>
<!-- <child>
<object class="GtkStackPage">
<property name="name">settings</property>
<property name="title" translatable="yes">page1</property>
<property name="position">1</property>
<property name="child">
<object class="GtkScrolledWindow" id="page_settings">
<property name="shadow-type">in</property>
<property name="child">
<object class="GtkViewport">
<property name="can-focus">0</property>
<property name="shadow-type">none</property>
<property name="child">
<object class="GtkBox">
<property name="can-focus">0</property>
<property name="margin-start">10</property>
<property name="margin-end">10</property>
<property name="margin-top">10</property>
<property name="margin-bottom">10</property>
<property name="orientation">vertical</property>
<property name="spacing">10</property>
<child>
<object class="GtkBox">
<property name="can-focus">0</property>
<child>
<object class="GtkButton" id="settings_back">
<property name="label" translatable="yes">Back</property>
<property name="receives-default">1</property>
<property name="margin-start">10</property>
<property name="margin-end">10</property>
<style>
<class name="suggested-action"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="can-focus">0</property>
<property name="label" translatable="yes">Settings aren&apos;t functional yet</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="can-focus">0</property>
<property name="orientation">vertical</property>
<property name="spacing">4</property>
<child>
<object class="GtkLabel">
<property name="can-focus">0</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Photos</property>
<style>
<class name="heading"/>
</style>
</object>
</child>
<child>
<object class="GtkFrame">
<property name="can-focus">0</property>
<property name="shadow-type">in</property>
<child>
<object class="GtkAlignment">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="left-padding">12</property>
<child>
<object class="GtkBox">
<property name="can-focus">0</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel">
<property name="can-focus">0</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Resolution</property>
</object>
</child>
<child>
<object class="GtkComboBox">
<property name="can-focus">0</property>
</object>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<object class="GtkLabel">
<property name="can-focus">0</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Storage mode</property>
</object>
</child>
<child>
<object class="GtkBox">
<property name="can-focus">0</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkCheckButton" id="store_vng">
<property name="label" translatable="yes">Debayer with VNG (slowest)</property>
<property name="active">1</property>
</object>
</child>
<child>
<object class="GtkCheckButton" id="store_simple">
<property name="label" translatable="yes">Debayer with linear interpolation</property>
<property name="group">store_vng</property>
</object>
</child>
<child>
<object class="GtkCheckButton" id="store_raw">
<property name="label" translatable="yes">Raw</property>
<property name="group">store_vng</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
<style>
<class name="view"/>
</style>
</object>
</child>
</object>
</child>
<child>
<placeholder/>
</child>
</object>
</property>
</object>
</property>
</object>
</property>
</object>
</child> -->
</object>
</property>
</object>
</interface>

6
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

6
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

6
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

11
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);
}

12
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);
}

49
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 = floor(half_pixel_coord);
vec2 arrangement = mod(pixel_coord, 2.0);
vec2 is_bottom_left = step(1.0, arrangement);
vec2 is_top_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;
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);
// Fast SRGB estimate. See https://mimosa-pudica.net/fast-gamma/
vec3 srgb_color = (vec3(1.138) * inversesqrt(color) - vec3(0.138)) * color;
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);
// Slow SRGB estimate
// vec3 srgb_color = pow(color, vec3(1.0 / 2.2));
gl_FragColor = vec4((color_matrix * srgb_color).bgr, 0);
gl_FragColor = vec4(color_matrix * srgb_color, 0);
}

29
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);
}

198
gl_quickpreview.c

@ -1,101 +1,28 @@
#include "camera.h"
#include "gl_quickpreview.h"
#include "gl_utils.h"
#include <stdint.h>
#include <stdlib.h>
#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;
};
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
// };
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()
{
@ -103,51 +30,22 @@ GLQuickPreview *gl_quick_preview_new()
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;
}

128
gl_utils.c

@ -0,0 +1,128 @@
#include "gl_utils.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
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;
}

37
gl_utils.h

@ -1,34 +1,13 @@
#pragma once
#include <GLES2/gl2.h>
#include <stddef.h>
#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();
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;
}
extern const GLfloat gl_quad_vertices[8];
extern const GLfloat gl_quad_texcoords[8];
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);

635
main.c

@ -18,11 +18,20 @@
#include <gtk/gtk.h>
#include <locale.h>
#include <zbar.h>
#include "gl_utils.h"
#include "camera_config.h"
#include "quickpreview.h"
#include "io_pipeline.h"
#include "process_pipeline.h"
#define RENDERDOC
#ifdef RENDERDOC
#include <dlfcn.h>
#include <renderdoc/app.h>
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),
};
blit_program = gl_link_program(blit_shaders, 2);
glBindAttribLocation(blit_program, 0, "vert");
glBindAttribLocation(blit_program, 1, "tex_coord");
check_gl();
// 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);
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);
}
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;
}
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);
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);
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);
}