It works!

This commit is contained in:
Benjamin Schaaf 2021-04-15 23:14:20 +10:00
parent aa8b2409d9
commit 337526e9b9
19 changed files with 1230 additions and 675 deletions

View File

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

View File

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

View File

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

335
camera.ui Normal file
View File

@ -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>

View File

@ -11,7 +11,7 @@ capture-rate=10
capture-fmt=BGGR8 capture-fmt=BGGR8
preview-width=1280 preview-width=1280
preview-height=720 preview-height=720
preview-rate=20 preview-rate=60
preview-fmt=BGGR8 preview-fmt=BGGR8
rotate=270 rotate=270
colormatrix=1.384,-0.3203,-0.0124,-0.2728,1.049,0.1556,-0.0506,0.2577,0.8050 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 media-driver=sun6i-csi
capture-width=1280 capture-width=1280
capture-height=960 capture-height=960
capture-rate=30 capture-rate=60
capture-fmt=BGGR8 capture-fmt=BGGR8
preview-width=1280 preview-width=1280
preview-height=960 preview-height=960
preview-rate=30 preview-rate=60
preview-fmt=BGGR8 preview-fmt=BGGR8
rotate=90 rotate=90
mirrored=true mirrored=true

View File

@ -11,7 +11,7 @@ capture-rate=10
capture-fmt=BGGR8 capture-fmt=BGGR8
preview-width=1280 preview-width=1280
preview-height=720 preview-height=720
preview-rate=20 preview-rate=60
preview-fmt=BGGR8 preview-fmt=BGGR8
rotate=270 rotate=270
colormatrix=1.384,-0.3203,-0.0124,-0.2728,1.049,0.1556,-0.0506,0.2577,0.8050 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 media-driver=sun6i-csi
capture-width=1280 capture-width=1280
capture-height=960 capture-height=960
capture-rate=30 capture-rate=60
capture-fmt=BGGR8 capture-fmt=BGGR8
preview-width=1280 preview-width=1280
preview-height=960 preview-height=960
preview-rate=30 preview-rate=60
preview-fmt=BGGR8 preview-fmt=BGGR8
rotate=90 rotate=90
mirrored=true mirrored=true

View File

@ -11,7 +11,7 @@ capture-rate=10
capture-fmt=BGGR8 capture-fmt=BGGR8
preview-width=1280 preview-width=1280
preview-height=720 preview-height=720
preview-rate=20 preview-rate=60
preview-fmt=BGGR8 preview-fmt=BGGR8
rotate=270 rotate=270
mirrored=false mirrored=false
@ -30,11 +30,11 @@ driver=gc2145
media-driver=sun6i-csi media-driver=sun6i-csi
capture-width=1280 capture-width=1280
capture-height=960 capture-height=960
capture-rate=30 capture-rate=60
capture-fmt=BGGR8 capture-fmt=BGGR8
preview-width=1280 preview-width=1280
preview-height=960 preview-height=960
preview-rate=30 preview-rate=60
preview-fmt=BGGR8 preview-fmt=BGGR8
rotate=90 rotate=90
mirrored=true mirrored=true

11
data/blit.frag Normal file
View File

@ -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 Normal file
View File

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

View File

@ -1,36 +1,43 @@
precision mediump float; precision highp float;
uniform sampler2D texture; uniform sampler2D texture;
// uniform sampler2D srgb_map; uniform sampler2D pixel_layout;
uniform mat3 color_matrix; uniform mat3 color_matrix;
varying vec2 uv1; uniform vec2 pixel_size;
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;
#define fetch(p) texture2D(texture, p).r #define fetch(p) texture2D(texture, p).r
void main() { void main() {
vec4 pixels = vec4( vec4 pixels = vec4(
fetch(uv1), fetch(top_left_uv),
fetch(vec2(uv1.x, uv2.y)), fetch(top_right_uv),
fetch(uv2), fetch(bottom_right_uv),
fetch(vec2(uv2.x, uv1.y))); 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( // vec3 color = mix(
mix(pixels.zyx, pixels.wzy, is_top_left.y), // mix(pixels.xyz, pixels.yzw, is_bottom_left.y),
mix(pixels.yzw, pixels.xyz, is_top_left.y), // mix(pixels.wzy, pixels.zyx, is_bottom_left.y),
is_top_left.x); // is_bottom_left.x);
vec3 color = pixels.zyx;
vec3 srgb_color = pow(color, vec3(1.0 / 2.2)); // Fast SRGB estimate. See https://mimosa-pudica.net/fast-gamma/
// vec3 srgb_color = vec3( vec3 srgb_color = (vec3(1.138) * inversesqrt(color) - vec3(0.138)) * color;
// texture2D(srgb_map, vec2(color.r, 0)).r,
// texture2D(srgb_map, vec2(color.g, 0)).r,
// texture2D(srgb_map, vec2(color.b, 0)).r);
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);
} }

View File

@ -1,21 +1,32 @@
precision mediump float; precision highp float;
attribute vec2 vert; attribute vec2 vert;
attribute vec2 tex_coord; attribute vec2 tex_coord;
uniform mat3 transform; uniform mat3 transform;
uniform vec2 pixel_size; uniform vec2 pixel_size;
uniform vec2 half_image_size;
varying vec2 uv1; // varying vec2 uv1;
varying vec2 uv2; // varying vec2 uv2;
varying vec2 pixel_coord;
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() { void main() {
uv1 = tex_coord - pixel_size / 2.0; top_left_uv = tex_coord - pixel_size / 2.0;
uv2 = tex_coord + pixel_size / 2.0; bottom_right_uv = tex_coord + pixel_size / 2.0;
uv1 += pixel_size; top_right_uv = vec2(top_left_uv.x, bottom_right_uv.y);
uv2 += pixel_size; bottom_left_uv = vec2(bottom_right_uv.x, top_left_uv.y);
pixel_coord = uv1 / pixel_size;
// 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); gl_Position = vec4(transform * vec3(vert, 1), 1);
} }

View File

@ -1,153 +1,51 @@
#include "camera.h" #include "camera.h"
#include "gl_quickpreview.h" #include "gl_quickpreview.h"
#include "gl_utils.h" #include "gl_utils.h"
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#define VERTEX_ATTRIBUTE 0 #define VERTEX_ATTRIBUTE 0
#define TEX_COORD_ATTRIBUTE 1 #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 { struct _GLQuickPreview {
GLuint frame_buffer; GLuint frame_buffer;
GLuint program; GLuint program;
GLuint uniform_transform; GLuint uniform_transform;
GLuint uniform_pixel_size; GLuint uniform_pixel_size;
GLuint uniform_half_image_size;
GLuint uniform_texture; GLuint uniform_texture;
GLuint uniform_pixel_layout;
GLuint uniform_srgb_map; GLuint uniform_srgb_map;
GLuint uniform_color_matrix; 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() GLQuickPreview *gl_quick_preview_new()
{ {
GLuint frame_buffer; GLuint frame_buffer;
glGenFramebuffers(1, &frame_buffer); glGenFramebuffers(1, &frame_buffer);
check_gl(); check_gl();
GLuint vert = load_shader("data/debayer.vert", GL_VERTEX_SHADER); GLuint shaders[] = {
GLuint frag = load_shader("data/debayer.frag", GL_FRAGMENT_SHADER); gl_load_shader("data/debayer.vert", GL_VERTEX_SHADER),
gl_load_shader("data/debayer.frag", GL_FRAGMENT_SHADER),
GLuint program = glCreateProgram(); };
glAttachShader(program, vert);
glAttachShader(program, frag);
GLuint program = gl_link_program(shaders, 2);
glBindAttribLocation(program, VERTEX_ATTRIBUTE, "vert"); glBindAttribLocation(program, VERTEX_ATTRIBUTE, "vert");
glBindAttribLocation(program, TEX_COORD_ATTRIBUTE, "tex_coord"); glBindAttribLocation(program, TEX_COORD_ATTRIBUTE, "tex_coord");
glLinkProgram(program);
check_gl(); 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); glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
check_gl(); check_gl();
// glBindTexture(GL_TEXTURE_2D, srgb_texture); GLuint pixel_layout_texture_id;
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glGenTextures(1, &pixel_layout_texture_id);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); check_gl();
// 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);
GLQuickPreview *self = malloc(sizeof(GLQuickPreview)); GLQuickPreview *self = malloc(sizeof(GLQuickPreview));
self->frame_buffer = frame_buffer; self->frame_buffer = frame_buffer;
@ -155,10 +53,18 @@ GLQuickPreview *gl_quick_preview_new()
self->uniform_transform = glGetUniformLocation(self->program, "transform"); self->uniform_transform = glGetUniformLocation(self->program, "transform");
self->uniform_pixel_size = glGetUniformLocation(self->program, "pixel_size"); 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_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_color_matrix = glGetUniformLocation(self->program, "color_matrix");
self->uniform_srgb_map = glGetUniformLocation(self->program, "srgb_map"); 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; return self;
} }
@ -187,6 +93,36 @@ gl_quick_preview(GLQuickPreview *self,
const float *colormatrix, const float *colormatrix,
const uint8_t blacklevel) 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)); assert(ql_quick_preview_supports_format(self, format));
glBindFramebuffer(GL_FRAMEBUFFER, self->frame_buffer); glBindFramebuffer(GL_FRAMEBUFFER, self->frame_buffer);
@ -202,7 +138,7 @@ gl_quick_preview(GLQuickPreview *self,
glUseProgram(self->program); glUseProgram(self->program);
check_gl(); check_gl();
GLfloat rotation_list[4] = { 1, 0, -1, 0 }; GLfloat rotation_list[4] = { 0, -1, 0, 1 };
int rotation_index = 4 - rotation / 90; int rotation_index = 4 - rotation / 90;
GLfloat sin_rot = rotation_list[rotation_index]; GLfloat sin_rot = rotation_list[rotation_index];
@ -216,6 +152,9 @@ gl_quick_preview(GLQuickPreview *self,
glUniformMatrix3fv(self->uniform_transform, 1, GL_FALSE, matrix); glUniformMatrix3fv(self->uniform_transform, 1, GL_FALSE, matrix);
check_gl(); 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_x = 1.0f / src_width;
GLfloat pixel_size_y = 1.0f / src_height; GLfloat pixel_size_y = 1.0f / src_height;
glUniform2f(self->uniform_pixel_size, pixel_size_x, pixel_size_y); 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); glUniform1i(self->uniform_texture, 0);
check_gl(); check_gl();
// glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1);
// glBindTexture(GL_TEXTURE_2D, self->srgb_texture); glBindTexture(GL_TEXTURE_2D, self->pixel_layout_texture_id);
// glUniform1i(self->uniform_srgb_map, 1); glUniform1i(self->uniform_pixel_layout, 1);
// check_gl(); check_gl();
if (colormatrix) if (colormatrix)
{ {
@ -250,14 +189,10 @@ gl_quick_preview(GLQuickPreview *self,
glUniformMatrix3fv(self->uniform_color_matrix, 1, GL_FALSE, identity); glUniformMatrix3fv(self->uniform_color_matrix, 1, GL_FALSE, identity);
} }
glVertexAttribPointer(VERTEX_ATTRIBUTE, 2, GL_FLOAT, 0, 0, square_vertices); glVertexAttribPointer(VERTEX_ATTRIBUTE, 2, GL_FLOAT, 0, 0, gl_quad_vertices);
check_gl();
glEnableVertexAttribArray(VERTEX_ATTRIBUTE); glEnableVertexAttribArray(VERTEX_ATTRIBUTE);
check_gl(); glVertexAttribPointer(TEX_COORD_ATTRIBUTE, 2, GL_FLOAT, 0, 0, gl_quad_texcoords);
glVertexAttribPointer(TEX_COORD_ATTRIBUTE, 2, GL_FLOAT, 0, 0, square_texcoords);
check_gl();
glEnableVertexAttribArray(TEX_COORD_ATTRIBUTE); glEnableVertexAttribArray(TEX_COORD_ATTRIBUTE);
check_gl();
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
check_gl(); check_gl();
@ -268,8 +203,5 @@ gl_quick_preview(GLQuickPreview *self,
// glBindTexture(GL_TEXTURE_2D, 0); // glBindTexture(GL_TEXTURE_2D, 0);
// glBindFramebuffer(GL_FRAMEBUFFER, 0); // glBindFramebuffer(GL_FRAMEBUFFER, 0);
glUseProgram(0);
check_gl();
return true; return true;
} }

128
gl_utils.c Normal file
View File

@ -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;
}

View File

@ -1,34 +1,13 @@
#pragma once
#include <GLES2/gl2.h> #include <GLES2/gl2.h>
#include <stddef.h>
#define check_gl() __check_gl(__FILE__, __LINE__) #define check_gl() __check_gl(__FILE__, __LINE__)
void __check_gl(const char *file, int line);
static void __check_gl(const char *file, int line) extern const GLfloat gl_quad_vertices[8];
{ extern const GLfloat gl_quad_texcoords[8];
GLenum error = glGetError();
const char *name; GLuint gl_load_shader(const char *path, GLenum type);
switch (error) { GLuint gl_link_program(GLuint *shaders, size_t num_shaders);
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);
}

599
main.c
View File

@ -18,11 +18,20 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <locale.h> #include <locale.h>
#include <zbar.h> #include <zbar.h>
#include "gl_utils.h"
#include "camera_config.h" #include "camera_config.h"
#include "quickpreview.h" #include "quickpreview.h"
#include "io_pipeline.h" #include "io_pipeline.h"
#include "process_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 }; enum user_control { USER_CONTROL_ISO, USER_CONTROL_SHUTTER };
static bool camera_is_initialized = false; static bool camera_is_initialized = false;
@ -42,7 +51,8 @@ static int exposure;
static bool has_auto_focus_continuous; static bool has_auto_focus_continuous;
static bool has_auto_focus_start; 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 cairo_surface_t *status_surface = NULL;
static char last_path[260] = ""; static char last_path[260] = "";
@ -157,21 +167,21 @@ void mp_main_set_zbar_result(MPZBarScanResult *result)
} }
static bool static bool
set_preview(cairo_surface_t *image) set_preview(MPProcessPipelineBuffer *buffer)
{ {
if (surface) { if (current_preview_buffer) {
cairo_surface_destroy(surface); mp_process_pipeline_buffer_unref(current_preview_buffer);
} }
surface = image; current_preview_buffer = buffer;
gtk_widget_queue_draw(preview); gtk_widget_queue_draw(preview);
return false; return false;
} }
void 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, 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, 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); 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_spinner_stop(GTK_SPINNER(process_spinner));
gtk_stack_set_visible_child(GTK_STACK(open_last_stack), thumb_last); 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 static void
draw_controls() draw_controls()
{ {
cairo_t *cr; // cairo_t *cr;
char iso[6]; char iso[6];
int temp; int temp;
char shutterangle[6]; char shutterangle[6];
@ -259,72 +269,124 @@ draw_controls()
cairo_surface_destroy(status_surface); cairo_surface_destroy(status_surface);
// Make a service to show status of controls, 32px high // 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; 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); GLuint blit_shaders[] = {
cairo_set_source_rgba(cr, 0, 0, 0, 0.0); gl_load_shader("data/blit.vert", GL_VERTEX_SHADER),
cairo_paint(cr); gl_load_shader("data/blit.frag", GL_FRAGMENT_SHADER),
};
// Draw the outlines for the headings blit_program = gl_link_program(blit_shaders, 2);
cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, glBindAttribLocation(blit_program, 0, "vert");
CAIRO_FONT_WEIGHT_BOLD); glBindAttribLocation(blit_program, 1, "tex_coord");
cairo_set_font_size(cr, 9); check_gl();
cairo_set_source_rgba(cr, 0, 0, 0, 1);
cairo_move_to(cr, 16, 16); blit_uniform_texture = glGetUniformLocation(blit_program, "texture");
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 gboolean 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) { if (!camera_is_initialized) {
return FALSE; 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 // Clear preview area with black
cairo_paint(cr); cairo_paint(cr);
@ -367,19 +429,23 @@ preview_draw(GtkWidget *widget, cairo_t *cr, gpointer data)
// Draw control overlay // Draw control overlay
cairo_set_source_surface(cr, status_surface, 0, 0); cairo_set_source_surface(cr, status_surface, 0, 0);
cairo_paint(cr); cairo_paint(cr);
*/
glFlush();
// #ifdef RENDERDOC
// if(rdoc_api) rdoc_api->EndFrameCapture(NULL, NULL);
// #endif
return FALSE; return FALSE;
} }
static gboolean 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); if (preview_width != width || preview_height != height) {
int new_preview_height = gtk_widget_get_allocated_height(widget); preview_width = width;
preview_height = height;
if (preview_width != new_preview_width ||
preview_height != new_preview_height) {
preview_width = new_preview_width;
preview_height = new_preview_height;
update_io_pipeline(); update_io_pipeline();
} }
@ -422,158 +488,158 @@ on_shutter_clicked(GtkWidget *widget, gpointer user_data)
mp_io_pipeline_capture(); mp_io_pipeline_capture();
} }
void // void
on_capture_shortcut(void) // on_capture_shortcut(void)
{ // {
on_shutter_clicked(NULL, NULL); // on_shutter_clicked(NULL, NULL);
} // }
static bool // static bool
check_point_inside_bounds(int x, int y, int *bounds_x, int *bounds_y) // check_point_inside_bounds(int x, int y, int *bounds_x, int *bounds_y)
{ // {
bool right = false, left = false, top = false, bottom = false; // bool right = false, left = false, top = false, bottom = false;
for (int i = 0; i < 4; ++i) { // for (int i = 0; i < 4; ++i) {
if (x <= bounds_x[i]) // if (x <= bounds_x[i])
left = true; // left = true;
if (x >= bounds_x[i]) // if (x >= bounds_x[i])
right = true; // right = true;
if (y <= bounds_y[i]) // if (y <= bounds_y[i])
top = true; // top = true;
if (y >= bounds_y[i]) // if (y >= bounds_y[i])
bottom = true; // bottom = true;
} // }
return right && left && top && bottom; // return right && left && top && bottom;
} // }
static void // static void
on_zbar_code_tapped(GtkWidget *widget, const MPZBarCode *code) // on_zbar_code_tapped(GtkWidget *widget, const MPZBarCode *code)
{ // {
GtkWidget *dialog; // GtkWidget *dialog;
GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT; // GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
bool data_is_url = g_uri_is_valid( // bool data_is_url = g_uri_is_valid(
code->data, G_URI_FLAGS_PARSE_RELAXED, NULL); // code->data, G_URI_FLAGS_PARSE_RELAXED, NULL);
char* data = strdup(code->data); // char* data = strdup(code->data);
if (data_is_url) { // if (data_is_url) {
dialog = gtk_message_dialog_new( // dialog = gtk_message_dialog_new(
GTK_WINDOW(gtk_widget_get_toplevel(widget)), // GTK_WINDOW(gtk_widget_get_toplevel(widget)),
flags, // flags,
GTK_MESSAGE_QUESTION, // GTK_MESSAGE_QUESTION,
GTK_BUTTONS_NONE, // GTK_BUTTONS_NONE,
"Found a URL '%s' encoded in a %s code.", // "Found a URL '%s' encoded in a %s code.",
code->data, // code->data,
code->type); // code->type);
gtk_dialog_add_buttons( // gtk_dialog_add_buttons(
GTK_DIALOG(dialog), // GTK_DIALOG(dialog),
"_Open URL", // "_Open URL",
GTK_RESPONSE_YES, // GTK_RESPONSE_YES,
NULL); // NULL);
} else { // } else {
dialog = gtk_message_dialog_new( // dialog = gtk_message_dialog_new(
GTK_WINDOW(gtk_widget_get_toplevel(widget)), // GTK_WINDOW(gtk_widget_get_toplevel(widget)),
flags, // flags,
GTK_MESSAGE_QUESTION, // GTK_MESSAGE_QUESTION,
GTK_BUTTONS_NONE, // GTK_BUTTONS_NONE,
"Found '%s' encoded in a %s code.", // "Found '%s' encoded in a %s code.",
code->data, // code->data,
code->type); // code->type);
} // }
gtk_dialog_add_buttons( // gtk_dialog_add_buttons(
GTK_DIALOG(dialog), // GTK_DIALOG(dialog),
"_Copy", // "_Copy",
GTK_RESPONSE_ACCEPT, // GTK_RESPONSE_ACCEPT,
"_Cancel", // "_Cancel",
GTK_RESPONSE_CANCEL, // GTK_RESPONSE_CANCEL,
NULL); // NULL);
int result = gtk_dialog_run(GTK_DIALOG(dialog)); // int result = gtk_dialog_run(GTK_DIALOG(dialog));
GError *error = NULL; // GError *error = NULL;
switch (result) { // switch (result) {
case GTK_RESPONSE_YES: // case GTK_RESPONSE_YES:
if (!g_app_info_launch_default_for_uri(data, // if (!g_app_info_launch_default_for_uri(data,
NULL, &error)) { // NULL, &error)) {
g_printerr("Could not launch application: %s\n", // g_printerr("Could not launch application: %s\n",
error->message); // error->message);
} // }
case GTK_RESPONSE_ACCEPT: // case GTK_RESPONSE_ACCEPT:
gtk_clipboard_set_text( // gtk_clipboard_set_text(
gtk_clipboard_get(GDK_SELECTION_PRIMARY), // gtk_clipboard_get(GDK_SELECTION_PRIMARY),
data, -1); // data, -1);
case GTK_RESPONSE_CANCEL: // case GTK_RESPONSE_CANCEL:
break; // break;
default: // default:
g_printerr("Wrong dialog result: %d\n", result); // g_printerr("Wrong dialog result: %d\n", result);
} // }
gtk_widget_destroy(dialog); // gtk_widget_destroy(dialog);
} // }
void // void
on_preview_tap(GtkWidget *widget, GdkEventButton *event, gpointer user_data) // on_preview_tap(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
{ // {
if (event->type != GDK_BUTTON_PRESS) // if (event->type != GDK_BUTTON_PRESS)
return; // return;
// Handle taps on the controls // // Handle taps on the controls
if (event->y < 32) { // if (event->y < 32) {
if (gtk_widget_is_visible(control_box)) { // if (gtk_widget_is_visible(control_box)) {
gtk_widget_hide(control_box); // gtk_widget_hide(control_box);
return; // return;
} else { // } else {
gtk_widget_show(control_box); // gtk_widget_show(control_box);
} // }
if (event->x < 60) { // if (event->x < 60) {
// ISO // // ISO
current_control = USER_CONTROL_ISO; // current_control = USER_CONTROL_ISO;
gtk_label_set_text(GTK_LABEL(control_name), "ISO"); // gtk_label_set_text(GTK_LABEL(control_name), "ISO");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto), // gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto),
!gain_is_manual); // !gain_is_manual);
gtk_adjustment_set_lower(control_slider, 0.0); // gtk_adjustment_set_lower(control_slider, 0.0);
gtk_adjustment_set_upper(control_slider, (float)gain_max); // gtk_adjustment_set_upper(control_slider, (float)gain_max);
gtk_adjustment_set_value(control_slider, (double)gain); // gtk_adjustment_set_value(control_slider, (double)gain);
} else if (event->x > 60 && event->x < 120) { // } else if (event->x > 60 && event->x < 120) {
// Shutter angle // // Shutter angle
current_control = USER_CONTROL_SHUTTER; // current_control = USER_CONTROL_SHUTTER;
gtk_label_set_text(GTK_LABEL(control_name), "Shutter"); // gtk_label_set_text(GTK_LABEL(control_name), "Shutter");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto), // gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto),
!exposure_is_manual); // !exposure_is_manual);
gtk_adjustment_set_lower(control_slider, 1.0); // gtk_adjustment_set_lower(control_slider, 1.0);
gtk_adjustment_set_upper(control_slider, 360.0); // gtk_adjustment_set_upper(control_slider, 360.0);
gtk_adjustment_set_value(control_slider, (double)exposure); // gtk_adjustment_set_value(control_slider, (double)exposure);
} // }
return; // return;
} // }
// Tapped zbar result // // Tapped zbar result
if (zbar_result) { // if (zbar_result) {
// Transform the event coordinates to the image // // Transform the event coordinates to the image
int width = cairo_image_surface_get_width(surface); // int width = cairo_image_surface_get_width(surface);
int height = cairo_image_surface_get_height(surface); // int height = cairo_image_surface_get_height(surface);
double scale = MIN(preview_width / (double)width, preview_height / (double)height); // double scale = MIN(preview_width / (double)width, preview_height / (double)height);
int x = (event->x - preview_width / 2) / scale + width / 2; // int x = (event->x - preview_width / 2) / scale + width / 2;
int y = (event->y - preview_height / 2) / scale + height / 2; // int y = (event->y - preview_height / 2) / scale + height / 2;
for (uint8_t i = 0; i < zbar_result->size; ++i) { // for (uint8_t i = 0; i < zbar_result->size; ++i) {
MPZBarCode *code = &zbar_result->codes[i]; // MPZBarCode *code = &zbar_result->codes[i];
if (check_point_inside_bounds(x, y, code->bounds_x, code->bounds_y)) { // if (check_point_inside_bounds(x, y, code->bounds_x, code->bounds_y)) {
on_zbar_code_tapped(widget, code); // on_zbar_code_tapped(widget, code);
return; // return;
} // }
} // }
} // }
// Tapped preview image itself, try focussing // // Tapped preview image itself, try focussing
if (has_auto_focus_start) { // if (has_auto_focus_start) {
mp_io_pipeline_focus(); // mp_io_pipeline_focus();
} // }
} // }
void void
on_error_close_clicked(GtkWidget *widget, gpointer user_data) on_error_close_clicked(GtkWidget *widget, gpointer user_data)
@ -690,22 +756,35 @@ on_control_slider_changed(GtkAdjustment *widget, gpointer user_data)
static void static void
on_realize(GtkWidget *window, gpointer *data) 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 typedef struct
main(int argc, char *argv[])
{ {
if (!mp_load_config()) GtkApplication parent_instance;
return 1; } 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", g_object_set(gtk_settings_get_default(), "gtk-application-prefer-dark-theme",
TRUE, NULL); 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 *window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
GtkWidget *shutter = GTK_WIDGET(gtk_builder_get_object(builder, "shutter")); 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")); GTK_ADJUSTMENT(gtk_builder_get_object(builder, "control_adj"));
control_auto = GTK_WIDGET(gtk_builder_get_object(builder, "control_auto")); 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, "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(shutter, "clicked", G_CALLBACK(on_shutter_clicked), NULL);
g_signal_connect(error_close, "clicked", G_CALLBACK(on_error_close_clicked), g_signal_connect(error_close, "clicked", G_CALLBACK(on_error_close_clicked),
NULL); NULL);
@ -748,14 +827,15 @@ main(int argc, char *argv[])
NULL); NULL);
g_signal_connect(open_directory, "clicked", g_signal_connect(open_directory, "clicked",
G_CALLBACK(on_open_directory_clicked), NULL); G_CALLBACK(on_open_directory_clicked), NULL);
g_signal_connect(preview, "draw", G_CALLBACK(preview_draw), NULL); g_signal_connect(preview, "realize", G_CALLBACK(preview_realize), NULL);
g_signal_connect(preview, "configure-event", G_CALLBACK(preview_configure), g_signal_connect(preview, "render", G_CALLBACK(preview_draw), NULL);
NULL); g_signal_connect(preview, "resize", G_CALLBACK(preview_resize),
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); 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_signal_connect(control_auto, "toggled",
G_CALLBACK(on_control_auto_toggled), NULL); G_CALLBACK(on_control_auto_toggled), NULL);
g_signal_connect(control_slider, "value-changed", g_signal_connect(control_slider, "value-changed",
@ -763,7 +843,7 @@ main(int argc, char *argv[])
GtkCssProvider *provider = gtk_css_provider_new(); GtkCssProvider *provider = gtk_css_provider_new();
if (access("camera.css", F_OK) != -1) { 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 { } else {
gtk_css_provider_load_from_resource( gtk_css_provider_load_from_resource(
provider, "/org/postmarketos/Megapixels/camera.css"); 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_context_add_provider(context, GTK_STYLE_PROVIDER(provider),
GTK_STYLE_PROVIDER_PRIORITY_USER); 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(); // GtkAccelGroup* accel_group = gtk_accel_group_new();
gtk_accel_group_connect(accel_group, // gtk_accel_group_connect(accel_group,
GDK_KEY_space, // GDK_KEY_space,
0, // 0,
0, // 0,
capture_shortcut); // 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(); mp_io_pipeline_start();
camera = mp_get_camera_config(0); camera = mp_get_camera_config(0);
update_io_pipeline(); update_io_pipeline();
gtk_application_add_window(GTK_APPLICATION(app), GTK_WINDOW(window));
gtk_widget_show(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(); 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; return 0;
} }

3
main.h
View File

@ -2,6 +2,7 @@
#include "camera_config.h" #include "camera_config.h"
#include "zbar_pipeline.h" #include "zbar_pipeline.h"
#include "process_pipeline.h"
#include "gtk/gtk.h" #include "gtk/gtk.h"
#define MP_MAIN_THUMB_SIZE 24 #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_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_capture_completed(cairo_surface_t *thumb, const char *fname);
void mp_main_set_zbar_result(MPZBarScanResult *result); void mp_main_set_zbar_result(MPZBarScanResult *result);

View File

@ -1,6 +1,6 @@
project('megapixels', 'c') project('megapixels', 'c')
gnome = import('gnome') gnome = import('gnome')
gtkdep = dependency('gtk+-3.0') gtkdep = dependency('gtk4')
tiff = dependency('libtiff-4') tiff = dependency('libtiff-4')
zbar = dependency('zbar') zbar = dependency('zbar')
threads = dependency('threads') threads = dependency('threads')
@ -34,6 +34,7 @@ executable('megapixels',
'ini.c', 'ini.c',
'quickpreview.c', 'quickpreview.c',
'gl_quickpreview.c', 'gl_quickpreview.c',
'gl_utils.c',
'camera.c', 'camera.c',
'device.c', 'device.c',
'pipeline.c', 'pipeline.c',

View File

@ -16,7 +16,7 @@
#include "gl_utils.h" #include "gl_utils.h"
#include <EGL/egl.h> #include <EGL/egl.h>
#include <EGL/eglext.h> #include <EGL/eglext.h>
#include <gdk/gdkwayland.h> // #include <gdk/gdkwayland.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <drm/drm_fourcc.h> #include <drm/drm_fourcc.h>
@ -151,130 +151,100 @@ mp_process_pipeline_sync()
#define NUM_BUFFERS 4 #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 GLQuickPreview *gl_quick_preview_state = NULL;
static EGLDisplay egl_display = EGL_NO_DISPLAY; static GdkGLContext *context;
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 PFNEGLEXPORTDMABUFIMAGEMESAPROC eglExportDMABUFImageMESA; static PFNEGLEXPORTDMABUFIMAGEMESAPROC eglExportDMABUFImageMESA;
static PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC eglExportDMABUFImageQueryMESA; static PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC eglExportDMABUFImageQueryMESA;
static const char * // static const char *
egl_get_error_str() // egl_get_error_str()
{ // {
EGLint error = eglGetError(); // EGLint error = eglGetError();
switch (error) { // switch (error) {
case EGL_SUCCESS: return "EGL_SUCCESS"; // case EGL_SUCCESS: return "EGL_SUCCESS";
case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED"; // case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED";
case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS"; // case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS";
case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC"; // case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC";
case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE"; // case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE";
case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT"; // case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT";
case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG"; // case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG";
case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE"; // case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; // case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY";
case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; // case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE";
case EGL_BAD_MATCH: return "EGL_BAD_MATCH"; // case EGL_BAD_MATCH: return "EGL_BAD_MATCH";
case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER"; // case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER";
case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP"; // case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW"; // case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST"; // case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST";
} // }
return "Unknown"; // return "Unknown";
} // }
#define RENDERDOC #define RENDERDOC
#ifdef RENDERDOC #ifdef RENDERDOC
#include <dlfcn.h>
#include <renderdoc/app.h> #include <renderdoc/app.h>
RENDERDOC_API_1_1_2 *rdoc_api = NULL; extern RENDERDOC_API_1_1_2 *rdoc_api;
#endif #endif
static void static void
init_gl(MPPipeline *pipeline, GdkWindow **window) init_gl(MPPipeline *pipeline, GdkSurface **surface)
{ {
#ifdef RENDERDOC GError *error = NULL;
{ context = gdk_surface_create_gl_context(*surface, &error);
void *mod = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD); if (context == NULL) {
if (mod) printf("Failed to initialize OpenGL context: %s\n", error->message);
{ g_clear_error(&error);
pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)dlsym(mod, "RENDERDOC_GetAPI"); return;
int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2, (void **)&rdoc_api);
assert(ret == 1);
}
else
{
printf("Renderdoc not found\n");
}
} }
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 #endif
GdkDisplay *gdk_display = gdk_window_get_display(*window); gdk_gl_context_realize(context, &error);
egl_display = eglGetDisplay((EGLNativeDisplayType) gdk_wayland_display_get_wl_display(gdk_display)); if (error != NULL) {
assert(egl_display != EGL_NO_DISPLAY); printf("Failed to create OpenGL context: %s\n", error->message);
g_clear_object(&context);
EGLint major, minor; g_clear_error(&error);
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());
return; return;
} }
gdk_gl_context_make_current(context);
check_gl(); check_gl();
eglExportDMABUFImageMESA = (PFNEGLEXPORTDMABUFIMAGEMESAPROC) eglExportDMABUFImageMESA = (PFNEGLEXPORTDMABUFIMAGEMESAPROC)
@ -300,153 +270,154 @@ init_gl(MPPipeline *pipeline, GdkWindow **window)
gl_quick_preview_state = gl_quick_preview_new(); gl_quick_preview_state = gl_quick_preview_new();
check_gl(); 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"); printf("Initialized OpenGL\n");
} }
void 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 * static cairo_surface_t *
process_image_for_preview(const uint8_t *image) process_image_for_preview(const uint8_t *image)
{ {
cairo_surface_t *surface;
clock_t t1 = clock(); clock_t t1 = clock();
if (gl_quick_preview_state && ql_quick_preview_supports_format(gl_quick_preview_state, mode.pixel_format)) { assert(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();
GLuint textures[2]; // Pick an available buffer
glGenTextures(2, textures); MPProcessPipelineBuffer *output_buffer = NULL;
for (size_t i = 0; i < NUM_BUFFERS; ++i) {
glBindTexture(GL_TEXTURE_2D, textures[0]); if (output_buffers[i].refcount == 0) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); output_buffer = &output_buffers[i];
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);
} }
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 // Create a thumbnail from the preview for the last capture
cairo_surface_t *thumb = NULL; cairo_surface_t *thumb = NULL;
if (captures_remaining == 1) { // if (captures_remaining == 1) {
printf("Making thumbnail\n"); // printf("Making thumbnail\n");
thumb = cairo_image_surface_create( // thumb = cairo_image_surface_create(
CAIRO_FORMAT_ARGB32, MP_MAIN_THUMB_SIZE, MP_MAIN_THUMB_SIZE); // CAIRO_FORMAT_ARGB32, MP_MAIN_THUMB_SIZE, MP_MAIN_THUMB_SIZE);
cairo_t *cr = cairo_create(thumb); // cairo_t *cr = cairo_create(thumb);
draw_surface_scaled_centered( // draw_surface_scaled_centered(
cr, MP_MAIN_THUMB_SIZE, MP_MAIN_THUMB_SIZE, surface); // cr, MP_MAIN_THUMB_SIZE, MP_MAIN_THUMB_SIZE, surface);
cairo_destroy(cr); // cairo_destroy(cr);
} // }
// Pass processed preview to main and zbar // Pass processed preview to main and zbar
mp_zbar_pipeline_process_image(cairo_surface_reference(surface)); // mp_zbar_pipeline_process_image(cairo_surface_reference(surface));
mp_main_set_preview(surface);
return thumb; return thumb;
} }
@ -755,17 +726,39 @@ mp_process_pipeline_capture()
mp_pipeline_invoke(pipeline, capture, NULL, 0); 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 static void
update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state) 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; camera = state->camera;
mode = state->mode; mode = state->mode;
burst_length = state->burst_length;
preview_width = state->preview_width; preview_width = state->preview_width;
preview_height = state->preview_height; preview_height = state->preview_height;
burst_length = state->burst_length;
// gain_is_manual = state->gain_is_manual; // gain_is_manual = state->gain_is_manual;
gain = state->gain; gain = state->gain;
gain_max = state->gain_max; 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_is_manual = state->exposure_is_manual;
exposure = state->exposure; exposure = state->exposure;
if (buffer_update_required) {
update_output_buffers();
}
struct mp_main_state main_state = { struct mp_main_state main_state = {
.camera = camera, .camera = camera,
.mode = mode, .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, mp_pipeline_invoke(pipeline, (MPPipelineCallback)update_state, new_state,
sizeof(struct mp_process_pipeline_state)); sizeof(struct mp_process_pipeline_state));
} }
// FUCK YOU GTK
void pango_fc_font_get_languages() {}

View File

@ -2,7 +2,7 @@
#include "camera_config.h" #include "camera_config.h"
typedef struct _GdkWindow GdkWindow; typedef struct _GdkSurface GdkSurface;
struct mp_process_pipeline_state { struct mp_process_pipeline_state {
const struct mp_camera_config *camera; const struct mp_camera_config *camera;
@ -28,8 +28,14 @@ void mp_process_pipeline_start();
void mp_process_pipeline_stop(); void mp_process_pipeline_stop();
void mp_process_pipeline_sync(); 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_process_image(MPBuffer buffer);
void mp_process_pipeline_capture(); void mp_process_pipeline_capture();
void mp_process_pipeline_update_state(const struct mp_process_pipeline_state *state); 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);