It works!
This commit is contained in:
parent
aa8b2409d9
commit
337526e9b9
1
camera.c
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
.black {
|
||||
background: #000000;
|
||||
}
|
||||
|
||||
.errorbox {
|
||||
background: #dd0000;
|
||||
color: #ffffff;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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'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>
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -1,36 +1,43 @@
|
|||
precision mediump float;
|
||||
precision highp float;
|
||||
|
||||
uniform sampler2D texture;
|
||||
// uniform sampler2D srgb_map;
|
||||
uniform sampler2D pixel_layout;
|
||||
uniform mat3 color_matrix;
|
||||
|
||||
varying vec2 uv1;
|
||||
varying vec2 uv2;
|
||||
varying vec2 pixel_coord;
|
||||
uniform vec2 pixel_size;
|
||||
|
||||
// varying vec2 uv1;
|
||||
// varying vec2 uv2;
|
||||
varying vec2 top_left_uv;
|
||||
varying vec2 top_right_uv;
|
||||
varying vec2 bottom_left_uv;
|
||||
varying vec2 bottom_right_uv;
|
||||
varying vec2 half_pixel_coord;
|
||||
|
||||
#define fetch(p) texture2D(texture, p).r
|
||||
|
||||
void main() {
|
||||
vec4 pixels = vec4(
|
||||
fetch(uv1),
|
||||
fetch(vec2(uv1.x, uv2.y)),
|
||||
fetch(uv2),
|
||||
fetch(vec2(uv2.x, uv1.y)));
|
||||
fetch(top_left_uv),
|
||||
fetch(top_right_uv),
|
||||
fetch(bottom_right_uv),
|
||||
fetch(bottom_left_uv));
|
||||
|
||||
vec2 arrangement = mod(pixel_coord, 2.0);
|
||||
vec2 arrangement = floor(half_pixel_coord);
|
||||
|
||||
vec2 is_top_left = step(1.0, arrangement);
|
||||
vec2 is_bottom_left = step(1.0, arrangement);
|
||||
|
||||
vec3 color = mix(
|
||||
mix(pixels.zyx, pixels.wzy, is_top_left.y),
|
||||
mix(pixels.yzw, pixels.xyz, is_top_left.y),
|
||||
is_top_left.x);
|
||||
// vec3 color = mix(
|
||||
// mix(pixels.xyz, pixels.yzw, is_bottom_left.y),
|
||||
// mix(pixels.wzy, pixels.zyx, is_bottom_left.y),
|
||||
// is_bottom_left.x);
|
||||
vec3 color = pixels.zyx;
|
||||
|
||||
vec3 srgb_color = pow(color, vec3(1.0 / 2.2));
|
||||
// vec3 srgb_color = vec3(
|
||||
// texture2D(srgb_map, vec2(color.r, 0)).r,
|
||||
// texture2D(srgb_map, vec2(color.g, 0)).r,
|
||||
// texture2D(srgb_map, vec2(color.b, 0)).r);
|
||||
// Fast SRGB estimate. See https://mimosa-pudica.net/fast-gamma/
|
||||
vec3 srgb_color = (vec3(1.138) * inversesqrt(color) - vec3(0.138)) * color;
|
||||
|
||||
gl_FragColor = vec4((color_matrix * srgb_color).bgr, 0);
|
||||
// Slow SRGB estimate
|
||||
// vec3 srgb_color = pow(color, vec3(1.0 / 2.2));
|
||||
|
||||
gl_FragColor = vec4(color_matrix * srgb_color, 0);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1,153 +1,51 @@
|
|||
#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;
|
||||
|
||||
GLuint pixel_layout_texture_id;
|
||||
uint32_t pixel_layout_texture_width;
|
||||
uint32_t pixel_layout_texture_height;
|
||||
MPPixelFormat pixel_layout_texture_format;
|
||||
};
|
||||
|
||||
static GLuint load_shader(const char *path, GLenum type)
|
||||
{
|
||||
check_gl();
|
||||
|
||||
FILE *f = fopen(path, "r");
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
GLint size = ftell(f);
|
||||
char *source = malloc(sizeof(char) * size);
|
||||
|
||||
fseek(f, 0, SEEK_SET);
|
||||
if (fread(source, size, 1, f) == 0) {
|
||||
printf("Failed to read shader file\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
GLuint shader = glCreateShader(type);
|
||||
assert(shader != 0);
|
||||
glShaderSource(shader, 1, (const GLchar * const*)&source, &size);
|
||||
glCompileShader(shader);
|
||||
check_gl();
|
||||
|
||||
// Check compile status
|
||||
GLint success;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (success == GL_FALSE) {
|
||||
printf("Shader compilation failed for %s\n", path);
|
||||
}
|
||||
|
||||
GLint log_length;
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
|
||||
if (log_length > 0) {
|
||||
char *log = malloc(sizeof(char) * log_length);
|
||||
glGetShaderInfoLog(shader, log_length - 1, &log_length, log);
|
||||
|
||||
printf("Shader %s log: %s\n", path, log);
|
||||
free(log);
|
||||
}
|
||||
|
||||
free(source);
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
// static const uint8_t srgb[] = {
|
||||
// 0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70,
|
||||
// 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, 96, 97, 99, 100,
|
||||
// 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118,
|
||||
// 120, 121, 122, 123, 124, 125, 126, 127, 129, 130, 131, 132, 133, 134,
|
||||
// 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147,
|
||||
// 148, 149, 150, 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159,
|
||||
// 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, 170,
|
||||
// 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180,
|
||||
// 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189,
|
||||
// 190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198,
|
||||
// 199, 199, 200, 201, 201, 202, 202, 203, 204, 204, 205, 205, 206, 206,
|
||||
// 207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214,
|
||||
// 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222,
|
||||
// 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, 228, 229, 229,
|
||||
// 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236,
|
||||
// 237, 237, 237, 238, 238, 239, 239, 240, 240, 241, 241, 242, 242, 243,
|
||||
// 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249,
|
||||
// 250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255
|
||||
// };
|
||||
|
||||
GLQuickPreview *gl_quick_preview_new()
|
||||
{
|
||||
GLuint frame_buffer;
|
||||
glGenFramebuffers(1, &frame_buffer);
|
||||
check_gl();
|
||||
|
||||
GLuint vert = load_shader("data/debayer.vert", GL_VERTEX_SHADER);
|
||||
GLuint frag = load_shader("data/debayer.frag", GL_FRAGMENT_SHADER);
|
||||
|
||||
GLuint program = glCreateProgram();
|
||||
glAttachShader(program, vert);
|
||||
glAttachShader(program, frag);
|
||||
GLuint shaders[] = {
|
||||
gl_load_shader("data/debayer.vert", GL_VERTEX_SHADER),
|
||||
gl_load_shader("data/debayer.frag", GL_FRAGMENT_SHADER),
|
||||
};
|
||||
|
||||
GLuint program = gl_link_program(shaders, 2);
|
||||
glBindAttribLocation(program, VERTEX_ATTRIBUTE, "vert");
|
||||
glBindAttribLocation(program, TEX_COORD_ATTRIBUTE, "tex_coord");
|
||||
glLinkProgram(program);
|
||||
check_gl();
|
||||
|
||||
GLint success;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &success);
|
||||
if (success == GL_FALSE) {
|
||||
printf("Program linking failed\n");
|
||||
}
|
||||
|
||||
GLint log_length;
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
|
||||
if (log_length > 0) {
|
||||
char *log = malloc(sizeof(char) * log_length);
|
||||
glGetProgramInfoLog(program, log_length - 1, &log_length, log);
|
||||
|
||||
printf("Program log: %s\n", log);
|
||||
free(log);
|
||||
}
|
||||
check_gl();
|
||||
|
||||
// GLuint srgb_texture;
|
||||
// glGenTextures(1, &srgb_texture);
|
||||
// check_gl();
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
check_gl();
|
||||
|
||||
// glBindTexture(GL_TEXTURE_2D, srgb_texture);
|
||||
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
// check_gl();
|
||||
// glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 256, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, srgb);
|
||||
// check_gl();
|
||||
// glBindTexture(GL_TEXTURE_2D, 0);
|
||||
GLuint pixel_layout_texture_id;
|
||||
glGenTextures(1, &pixel_layout_texture_id);
|
||||
check_gl();
|
||||
|
||||
GLQuickPreview *self = malloc(sizeof(GLQuickPreview));
|
||||
self->frame_buffer = frame_buffer;
|
||||
|
@ -155,10 +53,18 @@ GLQuickPreview *gl_quick_preview_new()
|
|||
|
||||
self->uniform_transform = glGetUniformLocation(self->program, "transform");
|
||||
self->uniform_pixel_size = glGetUniformLocation(self->program, "pixel_size");
|
||||
self->uniform_half_image_size = glGetUniformLocation(self->program, "half_image_size");
|
||||
self->uniform_texture = glGetUniformLocation(self->program, "texture");
|
||||
self->uniform_pixel_layout = glGetUniformLocation(self->program, "pixel_layout");
|
||||
self->uniform_color_matrix = glGetUniformLocation(self->program, "color_matrix");
|
||||
self->uniform_srgb_map = glGetUniformLocation(self->program, "srgb_map");
|
||||
// self->srgb_texture = srgb_texture;
|
||||
check_gl();
|
||||
|
||||
self->pixel_layout_texture_id = pixel_layout_texture_id;
|
||||
self->pixel_layout_texture_width = 0;
|
||||
self->pixel_layout_texture_height = 0;
|
||||
self->pixel_layout_texture_format = MP_PIXEL_FMT_BGGR8;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
@ -187,6 +93,36 @@ gl_quick_preview(GLQuickPreview *self,
|
|||
const float *colormatrix,
|
||||
const uint8_t blacklevel)
|
||||
{
|
||||
// Generate pixel layout image
|
||||
if (src_width != self->pixel_layout_texture_width
|
||||
|| src_height != self->pixel_layout_texture_height
|
||||
|| format != self->pixel_layout_texture_format) {
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, self->pixel_layout_texture_id);
|
||||
|
||||
uint16_t *buffer = malloc(src_width * src_height * sizeof(uint16_t));
|
||||
for (size_t y = 0; y < src_height; ++y) {
|
||||
for (size_t x = 0; x < src_width; ++x) {
|
||||
buffer[x + y * src_width] =
|
||||
(y % 2) == 0
|
||||
? ((x % 2) == 0 ? 0b1111 << 4 : 0b1111 << 8)
|
||||
: ((x % 2) == 0 ? 0b1111 << 8 : 0b1111 << 12);
|
||||
}
|
||||
}
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_width, src_height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, buffer);
|
||||
check_gl();
|
||||
free(buffer);
|
||||
|
||||
self->pixel_layout_texture_width = src_width;
|
||||
self->pixel_layout_texture_height = src_height;
|
||||
self->pixel_layout_texture_format = format;
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
check_gl();
|
||||
}
|
||||
|
||||
assert(ql_quick_preview_supports_format(self, format));
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, self->frame_buffer);
|
||||
|
@ -202,7 +138,7 @@ gl_quick_preview(GLQuickPreview *self,
|
|||
glUseProgram(self->program);
|
||||
check_gl();
|
||||
|
||||
GLfloat rotation_list[4] = { 1, 0, -1, 0 };
|
||||
GLfloat rotation_list[4] = { 0, -1, 0, 1 };
|
||||
int rotation_index = 4 - rotation / 90;
|
||||
|
||||
GLfloat sin_rot = rotation_list[rotation_index];
|
||||
|
@ -216,6 +152,9 @@ gl_quick_preview(GLQuickPreview *self,
|
|||
glUniformMatrix3fv(self->uniform_transform, 1, GL_FALSE, matrix);
|
||||
check_gl();
|
||||
|
||||
glUniform2f(self->uniform_half_image_size, src_width / 2, src_height / 2);
|
||||
check_gl();
|
||||
|
||||
GLfloat pixel_size_x = 1.0f / src_width;
|
||||
GLfloat pixel_size_y = 1.0f / src_height;
|
||||
glUniform2f(self->uniform_pixel_size, pixel_size_x, pixel_size_y);
|
||||
|
@ -226,10 +165,10 @@ gl_quick_preview(GLQuickPreview *self,
|
|||
glUniform1i(self->uniform_texture, 0);
|
||||
check_gl();
|
||||
|
||||
// glActiveTexture(GL_TEXTURE1);
|
||||
// glBindTexture(GL_TEXTURE_2D, self->srgb_texture);
|
||||
// glUniform1i(self->uniform_srgb_map, 1);
|
||||
// check_gl();
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, self->pixel_layout_texture_id);
|
||||
glUniform1i(self->uniform_pixel_layout, 1);
|
||||
check_gl();
|
||||
|
||||
if (colormatrix)
|
||||
{
|
||||
|
@ -250,14 +189,10 @@ gl_quick_preview(GLQuickPreview *self,
|
|||
glUniformMatrix3fv(self->uniform_color_matrix, 1, GL_FALSE, identity);
|
||||
}
|
||||
|
||||
glVertexAttribPointer(VERTEX_ATTRIBUTE, 2, GL_FLOAT, 0, 0, square_vertices);
|
||||
check_gl();
|
||||
glVertexAttribPointer(VERTEX_ATTRIBUTE, 2, GL_FLOAT, 0, 0, gl_quad_vertices);
|
||||
glEnableVertexAttribArray(VERTEX_ATTRIBUTE);
|
||||
check_gl();
|
||||
glVertexAttribPointer(TEX_COORD_ATTRIBUTE, 2, GL_FLOAT, 0, 0, square_texcoords);
|
||||
check_gl();
|
||||
glVertexAttribPointer(TEX_COORD_ATTRIBUTE, 2, GL_FLOAT, 0, 0, gl_quad_texcoords);
|
||||
glEnableVertexAttribArray(TEX_COORD_ATTRIBUTE);
|
||||
check_gl();
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
check_gl();
|
||||
|
||||
|
@ -268,8 +203,5 @@ gl_quick_preview(GLQuickPreview *self,
|
|||
// glBindTexture(GL_TEXTURE_2D, 0);
|
||||
// glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
glUseProgram(0);
|
||||
check_gl();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -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
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();
|
||||
extern const GLfloat gl_quad_vertices[8];
|
||||
extern const GLfloat gl_quad_texcoords[8];
|
||||
|
||||
const char *name;
|
||||
switch (error) {
|
||||
case GL_NO_ERROR:
|
||||
return; // no error
|
||||
case GL_INVALID_ENUM:
|
||||
name = "GL_INVALID_ENUM";
|
||||
break;
|
||||
case GL_INVALID_VALUE:
|
||||
name = "GL_INVALID_VALUE";
|
||||
break;
|
||||
case GL_INVALID_OPERATION:
|
||||
name = "GL_INVALID_OPERATION";
|
||||
break;
|
||||
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
||||
name = "GL_INVALID_FRAMEBUFFER_OPERATION";
|
||||
break;
|
||||
case GL_OUT_OF_MEMORY:
|
||||
name = "GL_OUT_OF_MEMORY";
|
||||
break;
|
||||
default:
|
||||
name = "UNKNOWN ERROR!";
|
||||
break;
|
||||
}
|
||||
|
||||
printf("GL error at %s:%d - %s\n", file, line, name);
|
||||
}
|
||||
GLuint gl_load_shader(const char *path, GLenum type);
|
||||
GLuint gl_link_program(GLuint *shaders, size_t num_shaders);
|
||||
|
|
599
main.c
599
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),
|
||||
};
|
||||
|
||||
// Draw the outlines for the headings
|
||||
cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL,
|
||||
CAIRO_FONT_WEIGHT_BOLD);
|
||||
cairo_set_font_size(cr, 9);
|
||||
cairo_set_source_rgba(cr, 0, 0, 0, 1);
|
||||
blit_program = gl_link_program(blit_shaders, 2);
|
||||
glBindAttribLocation(blit_program, 0, "vert");
|
||||
glBindAttribLocation(blit_program, 1, "tex_coord");
|
||||
check_gl();
|
||||
|
||||
cairo_move_to(cr, 16, 16);
|
||||
cairo_text_path(cr, "ISO");
|
||||
cairo_stroke(cr);
|
||||
|
||||
cairo_move_to(cr, 60, 16);
|
||||
cairo_text_path(cr, "Shutter");
|
||||
cairo_stroke(cr);
|
||||
|
||||
// Draw the fill for the headings
|
||||
cairo_set_source_rgba(cr, 1, 1, 1, 1);
|
||||
cairo_move_to(cr, 16, 16);
|
||||
cairo_show_text(cr, "ISO");
|
||||
cairo_move_to(cr, 60, 16);
|
||||
cairo_show_text(cr, "Shutter");
|
||||
|
||||
// Draw the outlines for the values
|
||||
cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL,
|
||||
CAIRO_FONT_WEIGHT_NORMAL);
|
||||
cairo_set_font_size(cr, 11);
|
||||
cairo_set_source_rgba(cr, 0, 0, 0, 1);
|
||||
|
||||
cairo_move_to(cr, 16, 26);
|
||||
cairo_text_path(cr, iso);
|
||||
cairo_stroke(cr);
|
||||
|
||||
cairo_move_to(cr, 60, 26);
|
||||
cairo_text_path(cr, shutterangle);
|
||||
cairo_stroke(cr);
|
||||
|
||||
// Draw the fill for the values
|
||||
cairo_set_source_rgba(cr, 1, 1, 1, 1);
|
||||
cairo_move_to(cr, 16, 26);
|
||||
cairo_show_text(cr, iso);
|
||||
cairo_move_to(cr, 60, 26);
|
||||
cairo_show_text(cr, shutterangle);
|
||||
|
||||
cairo_destroy(cr);
|
||||
|
||||
gtk_widget_queue_draw_area(preview, 0, 0, preview_width, 32);
|
||||
blit_uniform_texture = glGetUniformLocation(blit_program, "texture");
|
||||
}
|
||||
|
||||
static gboolean
|
||||
preview_draw(GtkWidget *widget, cairo_t *cr, gpointer data)
|
||||
preview_draw(GtkGLArea *area, GdkGLContext *ctx, gpointer data)
|
||||
{
|
||||
if (gtk_gl_area_get_error(area) != NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!camera_is_initialized) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// #ifdef RENDERDOC
|
||||
// if (rdoc_api) rdoc_api->StartFrameCapture(NULL, NULL);
|
||||
// #endif
|
||||
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (current_preview_buffer) {
|
||||
glUseProgram(blit_program);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, mp_process_pipeline_buffer_get_texture_id(current_preview_buffer));
|
||||
glUniform1i(blit_uniform_texture, 0);
|
||||
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, 0, 0, gl_quad_vertices);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, gl_quad_texcoords);
|
||||
glEnableVertexAttribArray(1);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
check_gl();
|
||||
}
|
||||
|
||||
/*
|
||||
// Clear preview area with black
|
||||
cairo_paint(cr);
|
||||
|
||||
|
@ -367,19 +429,23 @@ preview_draw(GtkWidget *widget, cairo_t *cr, gpointer data)
|
|||
// Draw control overlay
|
||||
cairo_set_source_surface(cr, status_surface, 0, 0);
|
||||
cairo_paint(cr);
|
||||
*/
|
||||
|
||||
glFlush();
|
||||
|
||||
// #ifdef RENDERDOC
|
||||
// if(rdoc_api) rdoc_api->EndFrameCapture(NULL, NULL);
|
||||
// #endif
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
preview_configure(GtkWidget *widget, GdkEventConfigure *event)
|
||||
preview_resize(GtkWidget *widget, int width, int height, gpointer data)
|
||||
{
|
||||
int new_preview_width = gtk_widget_get_allocated_width(widget);
|
||||
int new_preview_height = gtk_widget_get_allocated_height(widget);
|
||||
|
||||
if (preview_width != new_preview_width ||
|
||||
preview_height != new_preview_height) {
|
||||
preview_width = new_preview_width;
|
||||
preview_height = new_preview_height;
|
||||
if (preview_width != width || preview_height != height) {
|
||||
preview_width = width;
|
||||
preview_height = height;
|
||||
update_io_pipeline();
|
||||
}
|
||||
|
||||
|
@ -422,158 +488,158 @@ on_shutter_clicked(GtkWidget *widget, gpointer user_data)
|
|||
mp_io_pipeline_capture();
|
||||
}
|
||||
|
||||
void
|
||||
on_capture_shortcut(void)
|
||||
{
|
||||
on_shutter_clicked(NULL, NULL);
|
||||
}
|
||||
// void
|
||||
// on_capture_shortcut(void)
|
||||
// {
|
||||
// on_shutter_clicked(NULL, NULL);
|
||||
// }
|
||||
|
||||
static bool
|
||||
check_point_inside_bounds(int x, int y, int *bounds_x, int *bounds_y)
|
||||
{
|
||||
bool right = false, left = false, top = false, bottom = false;
|
||||
// static bool
|
||||
// check_point_inside_bounds(int x, int y, int *bounds_x, int *bounds_y)
|
||||
// {
|
||||
// bool right = false, left = false, top = false, bottom = false;
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (x <= bounds_x[i])
|
||||
left = true;
|
||||
if (x >= bounds_x[i])
|
||||
right = true;
|
||||
if (y <= bounds_y[i])
|
||||
top = true;
|
||||
if (y >= bounds_y[i])
|
||||
bottom = true;
|
||||
}
|
||||
// for (int i = 0; i < 4; ++i) {
|
||||
// if (x <= bounds_x[i])
|
||||
// left = true;
|
||||
// if (x >= bounds_x[i])
|
||||
// right = true;
|
||||
// if (y <= bounds_y[i])
|
||||
// top = true;
|
||||
// if (y >= bounds_y[i])
|
||||
// bottom = true;
|
||||
// }
|
||||
|
||||
return right && left && top && bottom;
|
||||
}
|
||||
// return right && left && top && bottom;
|
||||
// }
|
||||
|
||||
static void
|
||||
on_zbar_code_tapped(GtkWidget *widget, const MPZBarCode *code)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
|
||||
bool data_is_url = g_uri_is_valid(
|
||||
code->data, G_URI_FLAGS_PARSE_RELAXED, NULL);
|
||||
// static void
|
||||
// on_zbar_code_tapped(GtkWidget *widget, const MPZBarCode *code)
|
||||
// {
|
||||
// GtkWidget *dialog;
|
||||
// GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
|
||||
// bool data_is_url = g_uri_is_valid(
|
||||
// code->data, G_URI_FLAGS_PARSE_RELAXED, NULL);
|
||||
|
||||
char* data = strdup(code->data);
|
||||
// char* data = strdup(code->data);
|
||||
|
||||
if (data_is_url) {
|
||||
dialog = gtk_message_dialog_new(
|
||||
GTK_WINDOW(gtk_widget_get_toplevel(widget)),
|
||||
flags,
|
||||
GTK_MESSAGE_QUESTION,
|
||||
GTK_BUTTONS_NONE,
|
||||
"Found a URL '%s' encoded in a %s code.",
|
||||
code->data,
|
||||
code->type);
|
||||
gtk_dialog_add_buttons(
|
||||
GTK_DIALOG(dialog),
|
||||
"_Open URL",
|
||||
GTK_RESPONSE_YES,
|
||||
NULL);
|
||||
} else {
|
||||
dialog = gtk_message_dialog_new(
|
||||
GTK_WINDOW(gtk_widget_get_toplevel(widget)),
|
||||
flags,
|
||||
GTK_MESSAGE_QUESTION,
|
||||
GTK_BUTTONS_NONE,
|
||||
"Found '%s' encoded in a %s code.",
|
||||
code->data,
|
||||
code->type);
|
||||
}
|
||||
gtk_dialog_add_buttons(
|
||||
GTK_DIALOG(dialog),
|
||||
"_Copy",
|
||||
GTK_RESPONSE_ACCEPT,
|
||||
"_Cancel",
|
||||
GTK_RESPONSE_CANCEL,
|
||||
NULL);
|
||||
// if (data_is_url) {
|
||||
// dialog = gtk_message_dialog_new(
|
||||
// GTK_WINDOW(gtk_widget_get_toplevel(widget)),
|
||||
// flags,
|
||||
// GTK_MESSAGE_QUESTION,
|
||||
// GTK_BUTTONS_NONE,
|
||||
// "Found a URL '%s' encoded in a %s code.",
|
||||
// code->data,
|
||||
// code->type);
|
||||
// gtk_dialog_add_buttons(
|
||||
// GTK_DIALOG(dialog),
|
||||
// "_Open URL",
|
||||
// GTK_RESPONSE_YES,
|
||||
// NULL);
|
||||
// } else {
|
||||
// dialog = gtk_message_dialog_new(
|
||||
// GTK_WINDOW(gtk_widget_get_toplevel(widget)),
|
||||
// flags,
|
||||
// GTK_MESSAGE_QUESTION,
|
||||
// GTK_BUTTONS_NONE,
|
||||
// "Found '%s' encoded in a %s code.",
|
||||
// code->data,
|
||||
// code->type);
|
||||
// }
|
||||
// gtk_dialog_add_buttons(
|
||||
// GTK_DIALOG(dialog),
|
||||
// "_Copy",
|
||||
// GTK_RESPONSE_ACCEPT,
|
||||
// "_Cancel",
|
||||
// GTK_RESPONSE_CANCEL,
|
||||
// NULL);
|
||||
|
||||
int result = gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
// int result = gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
|
||||
GError *error = NULL;
|
||||
switch (result) {
|
||||
case GTK_RESPONSE_YES:
|
||||
if (!g_app_info_launch_default_for_uri(data,
|
||||
NULL, &error)) {
|
||||
g_printerr("Could not launch application: %s\n",
|
||||
error->message);
|
||||
}
|
||||
case GTK_RESPONSE_ACCEPT:
|
||||
gtk_clipboard_set_text(
|
||||
gtk_clipboard_get(GDK_SELECTION_PRIMARY),
|
||||
data, -1);
|
||||
case GTK_RESPONSE_CANCEL:
|
||||
break;
|
||||
default:
|
||||
g_printerr("Wrong dialog result: %d\n", result);
|
||||
}
|
||||
gtk_widget_destroy(dialog);
|
||||
}
|
||||
// GError *error = NULL;
|
||||
// switch (result) {
|
||||
// case GTK_RESPONSE_YES:
|
||||
// if (!g_app_info_launch_default_for_uri(data,
|
||||
// NULL, &error)) {
|
||||
// g_printerr("Could not launch application: %s\n",
|
||||
// error->message);
|
||||
// }
|
||||
// case GTK_RESPONSE_ACCEPT:
|
||||
// gtk_clipboard_set_text(
|
||||
// gtk_clipboard_get(GDK_SELECTION_PRIMARY),
|
||||
// data, -1);
|
||||
// case GTK_RESPONSE_CANCEL:
|
||||
// break;
|
||||
// default:
|
||||
// g_printerr("Wrong dialog result: %d\n", result);
|
||||
// }
|
||||
// gtk_widget_destroy(dialog);
|
||||
// }
|
||||
|
||||
void
|
||||
on_preview_tap(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
|
||||
{
|
||||
if (event->type != GDK_BUTTON_PRESS)
|
||||
return;
|
||||
// void
|
||||
// on_preview_tap(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
|
||||
// {
|
||||
// if (event->type != GDK_BUTTON_PRESS)
|
||||
// return;
|
||||
|
||||
// Handle taps on the controls
|
||||
if (event->y < 32) {
|
||||
if (gtk_widget_is_visible(control_box)) {
|
||||
gtk_widget_hide(control_box);
|
||||
return;
|
||||
} else {
|
||||
gtk_widget_show(control_box);
|
||||
}
|
||||
// // Handle taps on the controls
|
||||
// if (event->y < 32) {
|
||||
// if (gtk_widget_is_visible(control_box)) {
|
||||
// gtk_widget_hide(control_box);
|
||||
// return;
|
||||
// } else {
|
||||
// gtk_widget_show(control_box);
|
||||
// }
|
||||
|
||||
if (event->x < 60) {
|
||||
// ISO
|
||||
current_control = USER_CONTROL_ISO;
|
||||
gtk_label_set_text(GTK_LABEL(control_name), "ISO");
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto),
|
||||
!gain_is_manual);
|
||||
gtk_adjustment_set_lower(control_slider, 0.0);
|
||||
gtk_adjustment_set_upper(control_slider, (float)gain_max);
|
||||
gtk_adjustment_set_value(control_slider, (double)gain);
|
||||
// if (event->x < 60) {
|
||||
// // ISO
|
||||
// current_control = USER_CONTROL_ISO;
|
||||
// gtk_label_set_text(GTK_LABEL(control_name), "ISO");
|
||||
// gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto),
|
||||
// !gain_is_manual);
|
||||
// gtk_adjustment_set_lower(control_slider, 0.0);
|
||||
// gtk_adjustment_set_upper(control_slider, (float)gain_max);
|
||||
// gtk_adjustment_set_value(control_slider, (double)gain);
|
||||
|
||||
} else if (event->x > 60 && event->x < 120) {
|
||||
// Shutter angle
|
||||
current_control = USER_CONTROL_SHUTTER;
|
||||
gtk_label_set_text(GTK_LABEL(control_name), "Shutter");
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto),
|
||||
!exposure_is_manual);
|
||||
gtk_adjustment_set_lower(control_slider, 1.0);
|
||||
gtk_adjustment_set_upper(control_slider, 360.0);
|
||||
gtk_adjustment_set_value(control_slider, (double)exposure);
|
||||
}
|
||||
// } else if (event->x > 60 && event->x < 120) {
|
||||
// // Shutter angle
|
||||
// current_control = USER_CONTROL_SHUTTER;
|
||||
// gtk_label_set_text(GTK_LABEL(control_name), "Shutter");
|
||||
// gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto),
|
||||
// !exposure_is_manual);
|
||||
// gtk_adjustment_set_lower(control_slider, 1.0);
|
||||
// gtk_adjustment_set_upper(control_slider, 360.0);
|
||||
// gtk_adjustment_set_value(control_slider, (double)exposure);
|
||||
// }
|
||||
|
||||
return;
|
||||
}
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Tapped zbar result
|
||||
if (zbar_result) {
|
||||
// Transform the event coordinates to the image
|
||||
int width = cairo_image_surface_get_width(surface);
|
||||
int height = cairo_image_surface_get_height(surface);
|
||||
double scale = MIN(preview_width / (double)width, preview_height / (double)height);
|
||||
int x = (event->x - preview_width / 2) / scale + width / 2;
|
||||
int y = (event->y - preview_height / 2) / scale + height / 2;
|
||||
// // Tapped zbar result
|
||||
// if (zbar_result) {
|
||||
// // Transform the event coordinates to the image
|
||||
// int width = cairo_image_surface_get_width(surface);
|
||||
// int height = cairo_image_surface_get_height(surface);
|
||||
// double scale = MIN(preview_width / (double)width, preview_height / (double)height);
|
||||
// int x = (event->x - preview_width / 2) / scale + width / 2;
|
||||
// int y = (event->y - preview_height / 2) / scale + height / 2;
|
||||
|
||||
for (uint8_t i = 0; i < zbar_result->size; ++i) {
|
||||
MPZBarCode *code = &zbar_result->codes[i];
|
||||
// for (uint8_t i = 0; i < zbar_result->size; ++i) {
|
||||
// MPZBarCode *code = &zbar_result->codes[i];
|
||||
|
||||
if (check_point_inside_bounds(x, y, code->bounds_x, code->bounds_y)) {
|
||||
on_zbar_code_tapped(widget, code);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (check_point_inside_bounds(x, y, code->bounds_x, code->bounds_y)) {
|
||||
// on_zbar_code_tapped(widget, code);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// Tapped preview image itself, try focussing
|
||||
if (has_auto_focus_start) {
|
||||
mp_io_pipeline_focus();
|
||||
}
|
||||
}
|
||||
// // Tapped preview image itself, try focussing
|
||||
// if (has_auto_focus_start) {
|
||||
// mp_io_pipeline_focus();
|
||||
// }
|
||||
// }
|
||||
|
||||
void
|
||||
on_error_close_clicked(GtkWidget *widget, gpointer user_data)
|
||||
|
@ -690,22 +756,35 @@ on_control_slider_changed(GtkAdjustment *widget, gpointer user_data)
|
|||
static void
|
||||
on_realize(GtkWidget *window, gpointer *data)
|
||||
{
|
||||
mp_process_pipeline_init_gl(gtk_widget_get_window(window));
|
||||
GtkNative *native = gtk_widget_get_native(window);
|
||||
mp_process_pipeline_init_gl(gtk_native_get_surface(native));
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
typedef struct
|
||||
{
|
||||
if (!mp_load_config())
|
||||
return 1;
|
||||
GtkApplication parent_instance;
|
||||
} MegapixelsApp;
|
||||
|
||||
setenv("LC_NUMERIC", "C", 1);
|
||||
typedef GtkApplicationClass MegapixelsAppClass;
|
||||
|
||||
GType megapixels_app_get_type (void);
|
||||
G_DEFINE_TYPE(MegapixelsApp, megapixels_app, GTK_TYPE_APPLICATION)
|
||||
|
||||
static void
|
||||
startup(GApplication *app)
|
||||
{
|
||||
G_APPLICATION_CLASS(megapixels_app_parent_class)->startup(app);
|
||||
|
||||
gtk_init(&argc, &argv);
|
||||
g_object_set(gtk_settings_get_default(), "gtk-application-prefer-dark-theme",
|
||||
TRUE, NULL);
|
||||
GtkBuilder *builder = gtk_builder_new_from_resource(
|
||||
"/org/postmarketos/Megapixels/camera.glade");
|
||||
|
||||
GtkBuilder *builder;
|
||||
if (access("camera.ui", F_OK) != -1) {
|
||||
builder = gtk_builder_new_from_file("camera.ui");
|
||||
} else {
|
||||
builder = gtk_builder_new_from_file(
|
||||
"/org/postmarketos/Megapixels/camera.ui");
|
||||
}
|
||||
|
||||
GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
|
||||
GtkWidget *shutter = GTK_WIDGET(gtk_builder_get_object(builder, "shutter"));
|
||||
|
@ -734,7 +813,7 @@ main(int argc, char *argv[])
|
|||
GTK_ADJUSTMENT(gtk_builder_get_object(builder, "control_adj"));
|
||||
control_auto = GTK_WIDGET(gtk_builder_get_object(builder, "control_auto"));
|
||||
g_signal_connect(window, "realize", G_CALLBACK(on_realize), NULL);
|
||||
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
|
||||
// g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
|
||||
g_signal_connect(shutter, "clicked", G_CALLBACK(on_shutter_clicked), NULL);
|
||||
g_signal_connect(error_close, "clicked", G_CALLBACK(on_error_close_clicked),
|
||||
NULL);
|
||||
|
@ -748,14 +827,15 @@ main(int argc, char *argv[])
|
|||
NULL);
|
||||
g_signal_connect(open_directory, "clicked",
|
||||
G_CALLBACK(on_open_directory_clicked), NULL);
|
||||
g_signal_connect(preview, "draw", G_CALLBACK(preview_draw), NULL);
|
||||
g_signal_connect(preview, "configure-event", G_CALLBACK(preview_configure),
|
||||
NULL);
|
||||
gtk_widget_set_events(preview, gtk_widget_get_events(preview) |
|
||||
GDK_BUTTON_PRESS_MASK |
|
||||
GDK_POINTER_MOTION_MASK);
|
||||
g_signal_connect(preview, "button-press-event", G_CALLBACK(on_preview_tap),
|
||||
g_signal_connect(preview, "realize", G_CALLBACK(preview_realize), NULL);
|
||||
g_signal_connect(preview, "render", G_CALLBACK(preview_draw), NULL);
|
||||
g_signal_connect(preview, "resize", G_CALLBACK(preview_resize),
|
||||
NULL);
|
||||
// gtk_widget_set_events(preview, gtk_widget_get_events(preview) |
|
||||
// GDK_BUTTON_PRESS_MASK |
|
||||
// GDK_POINTER_MOTION_MASK);
|
||||
// g_signal_connect(preview, "button-press-event", G_CALLBACK(on_preview_tap),
|
||||
// NULL);
|
||||
g_signal_connect(control_auto, "toggled",
|
||||
G_CALLBACK(on_control_auto_toggled), NULL);
|
||||
g_signal_connect(control_slider, "value-changed",
|
||||
|
@ -763,7 +843,7 @@ main(int argc, char *argv[])
|
|||
|
||||
GtkCssProvider *provider = gtk_css_provider_new();
|
||||
if (access("camera.css", F_OK) != -1) {
|
||||
gtk_css_provider_load_from_path(provider, "camera.css", NULL);
|
||||
gtk_css_provider_load_from_path(provider, "camera.css");
|
||||
} else {
|
||||
gtk_css_provider_load_from_resource(
|
||||
provider, "/org/postmarketos/Megapixels/camera.css");
|
||||
|
@ -775,26 +855,81 @@ main(int argc, char *argv[])
|
|||
gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||
|
||||
GClosure* capture_shortcut = g_cclosure_new(on_capture_shortcut, 0, 0);
|
||||
// GClosure* capture_shortcut = g_cclosure_new(on_capture_shortcut, 0, 0);
|
||||
|
||||
GtkAccelGroup* accel_group = gtk_accel_group_new();
|
||||
gtk_accel_group_connect(accel_group,
|
||||
GDK_KEY_space,
|
||||
0,
|
||||
0,
|
||||
capture_shortcut);
|
||||
// GtkAccelGroup* accel_group = gtk_accel_group_new();
|
||||
// gtk_accel_group_connect(accel_group,
|
||||
// GDK_KEY_space,
|
||||
// 0,
|
||||
// 0,
|
||||
// capture_shortcut);
|
||||
|
||||
gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
|
||||
// gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
|
||||
|
||||
mp_io_pipeline_start();
|
||||
|
||||
camera = mp_get_camera_config(0);
|
||||
update_io_pipeline();
|
||||
|
||||
gtk_application_add_window(GTK_APPLICATION(app), GTK_WINDOW(window));
|
||||
gtk_widget_show(window);
|
||||
gtk_main();
|
||||
}
|
||||
|
||||
static void
|
||||
shutdown(GApplication *app)
|
||||
{
|
||||
// Only do cleanup in development, let the OS clean up otherwise
|
||||
#ifdef DEBUG
|
||||
mp_io_pipeline_stop();
|
||||
#endif
|
||||
|
||||
G_APPLICATION_CLASS(megapixels_app_parent_class)->shutdown(app);
|
||||
}
|
||||
|
||||
static void
|
||||
megapixels_app_init(MegapixelsApp *app)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
megapixels_app_class_init(MegapixelsAppClass *class)
|
||||
{
|
||||
GApplicationClass *application_class = G_APPLICATION_CLASS(class);
|
||||
|
||||
application_class->startup = startup;
|
||||
application_class->shutdown = shutdown;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
#ifdef RENDERDOC
|
||||
{
|
||||
void *mod = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD);
|
||||
if (mod)
|
||||
{
|
||||
pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)dlsym(mod, "RENDERDOC_GetAPI");
|
||||
int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2, (void **)&rdoc_api);
|
||||
assert(ret == 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Renderdoc not found\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!mp_load_config())
|
||||
return 1;
|
||||
|
||||
setenv("LC_NUMERIC", "C", 1);
|
||||
|
||||
MegapixelsApp *app = g_object_new(
|
||||
megapixels_app_get_type(),
|
||||
"application-id", "org.postmarketos.Megapixels",
|
||||
NULL);
|
||||
|
||||
g_application_run(G_APPLICATION(app), argc, argv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
3
main.h
3
main.h
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "camera_config.h"
|
||||
#include "zbar_pipeline.h"
|
||||
#include "process_pipeline.h"
|
||||
#include "gtk/gtk.h"
|
||||
|
||||
#define MP_MAIN_THUMB_SIZE 24
|
||||
|
@ -23,7 +24,7 @@ struct mp_main_state {
|
|||
|
||||
void mp_main_update_state(const struct mp_main_state *state);
|
||||
|
||||
void mp_main_set_preview(cairo_surface_t *image);
|
||||
void mp_main_set_preview(MPProcessPipelineBuffer *buffer);
|
||||
void mp_main_capture_completed(cairo_surface_t *thumb, const char *fname);
|
||||
|
||||
void mp_main_set_zbar_result(MPZBarScanResult *result);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
project('megapixels', 'c')
|
||||
gnome = import('gnome')
|
||||
gtkdep = dependency('gtk+-3.0')
|
||||
gtkdep = dependency('gtk4')
|
||||
tiff = dependency('libtiff-4')
|
||||
zbar = dependency('zbar')
|
||||
threads = dependency('threads')
|
||||
|
@ -34,6 +34,7 @@ executable('megapixels',
|
|||
'ini.c',
|
||||
'quickpreview.c',
|
||||
'gl_quickpreview.c',
|
||||
'gl_utils.c',
|
||||
'camera.c',
|
||||
'device.c',
|
||||
'pipeline.c',
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include "gl_utils.h"
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <gdk/gdkwayland.h>
|
||||
// #include <gdk/gdkwayland.h>
|
||||
#include <sys/mman.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
|
||||
|
@ -151,130 +151,100 @@ mp_process_pipeline_sync()
|
|||
|
||||
#define NUM_BUFFERS 4
|
||||
|
||||
struct _MPProcessPipelineBuffer {
|
||||
GLuint texture_id;
|
||||
|
||||
_Atomic(int) refcount;
|
||||
};
|
||||
static MPProcessPipelineBuffer output_buffers[NUM_BUFFERS];
|
||||
|
||||
static int output_buffer_width = 0;
|
||||
static int output_buffer_height = 0;
|
||||
|
||||
void
|
||||
mp_process_pipeline_buffer_ref(MPProcessPipelineBuffer *buf)
|
||||
{
|
||||
++buf->refcount;
|
||||
}
|
||||
|
||||
void
|
||||
mp_process_pipeline_buffer_unref(MPProcessPipelineBuffer *buf)
|
||||
{
|
||||
--buf->refcount;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mp_process_pipeline_buffer_get_texture_id(MPProcessPipelineBuffer *buf)
|
||||
{
|
||||
return buf->texture_id;
|
||||
}
|
||||
|
||||
static GLQuickPreview *gl_quick_preview_state = NULL;
|
||||
|
||||
static EGLDisplay egl_display = EGL_NO_DISPLAY;
|
||||
static EGLContext egl_context = EGL_NO_CONTEXT;
|
||||
|
||||
// struct buffer {
|
||||
// GLuint texture_id;
|
||||
// EGLImage egl_image;
|
||||
// int dma_fd;
|
||||
// int dma_stride;
|
||||
// int dma_offset;
|
||||
// void *data;
|
||||
// };
|
||||
// static struct buffer input_buffers[NUM_BUFFERS];
|
||||
// static struct buffer output_buffers[NUM_BUFFERS];
|
||||
static GdkGLContext *context;
|
||||
|
||||
static PFNEGLEXPORTDMABUFIMAGEMESAPROC eglExportDMABUFImageMESA;
|
||||
static PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC eglExportDMABUFImageQueryMESA;
|
||||
|
||||
static const char *
|
||||
egl_get_error_str()
|
||||
{
|
||||
EGLint error = eglGetError();
|
||||
switch (error) {
|
||||
case EGL_SUCCESS: return "EGL_SUCCESS";
|
||||
case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED";
|
||||
case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS";
|
||||
case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC";
|
||||
case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE";
|
||||
case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT";
|
||||
case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG";
|
||||
case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
|
||||
case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY";
|
||||
case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE";
|
||||
case EGL_BAD_MATCH: return "EGL_BAD_MATCH";
|
||||
case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER";
|
||||
case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
|
||||
case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
|
||||
case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
// static const char *
|
||||
// egl_get_error_str()
|
||||
// {
|
||||
// EGLint error = eglGetError();
|
||||
// switch (error) {
|
||||
// case EGL_SUCCESS: return "EGL_SUCCESS";
|
||||
// case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED";
|
||||
// case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS";
|
||||
// case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC";
|
||||
// case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE";
|
||||
// case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT";
|
||||
// case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG";
|
||||
// case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
|
||||
// case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY";
|
||||
// case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE";
|
||||
// case EGL_BAD_MATCH: return "EGL_BAD_MATCH";
|
||||
// case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER";
|
||||
// case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
|
||||
// case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
|
||||
// case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST";
|
||||
// }
|
||||
// return "Unknown";
|
||||
// }
|
||||
|
||||
#define RENDERDOC
|
||||
|
||||
#ifdef RENDERDOC
|
||||
#include <dlfcn.h>
|
||||
#include <renderdoc/app.h>
|
||||
RENDERDOC_API_1_1_2 *rdoc_api = NULL;
|
||||
extern RENDERDOC_API_1_1_2 *rdoc_api;
|
||||
#endif
|
||||
|
||||
static void
|
||||
init_gl(MPPipeline *pipeline, GdkWindow **window)
|
||||
init_gl(MPPipeline *pipeline, GdkSurface **surface)
|
||||
{
|
||||
#ifdef RENDERDOC
|
||||
{
|
||||
void *mod = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD);
|
||||
if (mod)
|
||||
{
|
||||
pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)dlsym(mod, "RENDERDOC_GetAPI");
|
||||
int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2, (void **)&rdoc_api);
|
||||
assert(ret == 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Renderdoc not found\n");
|
||||
}
|
||||
GError *error = NULL;
|
||||
context = gdk_surface_create_gl_context(*surface, &error);
|
||||
if (context == NULL) {
|
||||
printf("Failed to initialize OpenGL context: %s\n", error->message);
|
||||
g_clear_error(&error);
|
||||
return;
|
||||
}
|
||||
|
||||
gdk_gl_context_set_use_es(context, true);
|
||||
gdk_gl_context_set_required_version(context, 2, 0);
|
||||
#ifdef DEBUG
|
||||
gdk_gl_context_set_debug_enabled(context, true);
|
||||
#else
|
||||
gdk_gl_context_set_debug_enabled(context, false);
|
||||
#endif
|
||||
|
||||
GdkDisplay *gdk_display = gdk_window_get_display(*window);
|
||||
egl_display = eglGetDisplay((EGLNativeDisplayType) gdk_wayland_display_get_wl_display(gdk_display));
|
||||
assert(egl_display != EGL_NO_DISPLAY);
|
||||
|
||||
EGLint major, minor;
|
||||
if (!eglInitialize(egl_display, &major, &minor)) {
|
||||
printf("Failed to initialize egl: %s\n", egl_get_error_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!eglBindAPI(EGL_OPENGL_ES_API)) {
|
||||
printf("Failed to bind OpenGL ES: %s\n", egl_get_error_str());
|
||||
return;
|
||||
}
|
||||
|
||||
printf("extensions: %s\n", eglQueryString(egl_display, EGL_EXTENSIONS));
|
||||
|
||||
EGLint config_attrs[1] = {
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
EGLConfig config;
|
||||
EGLint num_configs = 0;
|
||||
if (!eglChooseConfig(egl_display, config_attrs, &config, 1, &num_configs)) {
|
||||
printf("Failed to pick a egl config: %s\n", egl_get_error_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (num_configs != 1) {
|
||||
printf("No egl configs found: %s\n", egl_get_error_str());
|
||||
return;
|
||||
}
|
||||
|
||||
EGLint context_attrs[5] = {
|
||||
EGL_CONTEXT_CLIENT_VERSION,
|
||||
2,
|
||||
EGL_CONTEXT_FLAGS_KHR,
|
||||
EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR,
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
egl_context = eglCreateContext(egl_display, config, NULL, context_attrs);
|
||||
|
||||
if (egl_context == EGL_NO_CONTEXT) {
|
||||
printf("Failed to create OpenGL ES context: %s\n", egl_get_error_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_context))
|
||||
{
|
||||
printf("Failed to set current OpenGL context: %s\n", egl_get_error_str());
|
||||
gdk_gl_context_realize(context, &error);
|
||||
if (error != NULL) {
|
||||
printf("Failed to create OpenGL context: %s\n", error->message);
|
||||
g_clear_object(&context);
|
||||
g_clear_error(&error);
|
||||
return;
|
||||
}
|
||||
|
||||
gdk_gl_context_make_current(context);
|
||||
check_gl();
|
||||
|
||||
eglExportDMABUFImageMESA = (PFNEGLEXPORTDMABUFIMAGEMESAPROC)
|
||||
|
@ -300,153 +270,154 @@ init_gl(MPPipeline *pipeline, GdkWindow **window)
|
|||
gl_quick_preview_state = gl_quick_preview_new();
|
||||
check_gl();
|
||||
|
||||
for (size_t i = 0; i < NUM_BUFFERS; ++i) {
|
||||
glGenTextures(1, &output_buffers[i].texture_id);
|
||||
glBindTexture(GL_TEXTURE_2D, output_buffers[i].texture_id);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
printf("Initialized OpenGL\n");
|
||||
}
|
||||
|
||||
void
|
||||
mp_process_pipeline_init_gl(GdkWindow *window)
|
||||
mp_process_pipeline_init_gl(GdkSurface *surface)
|
||||
{
|
||||
mp_pipeline_invoke(pipeline, (MPPipelineCallback) init_gl, &window, sizeof(GdkWindow *));
|
||||
mp_pipeline_invoke(pipeline, (MPPipelineCallback) init_gl, &surface, sizeof(GdkSurface *));
|
||||
}
|
||||
|
||||
static cairo_surface_t *
|
||||
process_image_for_preview(const uint8_t *image)
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
|
||||
clock_t t1 = clock();
|
||||
|
||||
if (gl_quick_preview_state && ql_quick_preview_supports_format(gl_quick_preview_state, mode.pixel_format)) {
|
||||
#ifdef RENDERDOC
|
||||
if (rdoc_api) rdoc_api->StartFrameCapture(NULL, NULL);
|
||||
#endif
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
check_gl();
|
||||
assert(gl_quick_preview_state && ql_quick_preview_supports_format(gl_quick_preview_state, mode.pixel_format));
|
||||
|
||||
GLuint textures[2];
|
||||
glGenTextures(2, textures);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, textures[0]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, mode.width, mode.height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image);
|
||||
check_gl();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, textures[1]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, preview_width, preview_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
check_gl();
|
||||
|
||||
gl_quick_preview(
|
||||
gl_quick_preview_state,
|
||||
textures[1], preview_width, preview_height,
|
||||
textures[0], mode.width, mode.height,
|
||||
mode.pixel_format,
|
||||
camera->rotate, camera->mirrored,
|
||||
camera->previewmatrix[0] == 0 ? NULL : camera->previewmatrix,
|
||||
camera->blacklevel);
|
||||
check_gl();
|
||||
|
||||
surface = cairo_image_surface_create(
|
||||
CAIRO_FORMAT_RGB24, preview_width, preview_height);
|
||||
uint32_t *pixels = (uint32_t *)cairo_image_surface_get_data(surface);
|
||||
glFinish();
|
||||
|
||||
clock_t t2 = clock();
|
||||
printf("%fms\n", (float)(t2 - t1) / CLOCKS_PER_SEC * 1000);
|
||||
|
||||
// {
|
||||
// glBindTexture(GL_TEXTURE_2D, textures[1]);
|
||||
// EGLImage egl_image = eglCreateImage(egl_display, egl_context, EGL_GL_TEXTURE_2D, (EGLClientBuffer)(size_t)textures[1], NULL);
|
||||
|
||||
// // Make sure it's in the expected format
|
||||
// int fourcc;
|
||||
// eglExportDMABUFImageQueryMESA(egl_display, egl_image, &fourcc, NULL, NULL);
|
||||
// assert(fourcc == DRM_FORMAT_ABGR8888);
|
||||
|
||||
|
||||
// int dmabuf_fd;
|
||||
// int stride, offset;
|
||||
// eglExportDMABUFImageMESA(egl_display, egl_image, &dmabuf_fd, &stride, &offset);
|
||||
|
||||
// int fsize = lseek(dmabuf_fd, 0, SEEK_END);
|
||||
// printf("SIZE %d STRIDE %d OFFSET %d SIZE %d:%d\n", fsize, stride, offset, preview_width, preview_height);
|
||||
|
||||
// size_t size = stride * preview_height;
|
||||
// uint32_t *data = mmap(NULL, fsize, PROT_READ, MAP_SHARED, dmabuf_fd, 0);
|
||||
// assert(data != MAP_FAILED);
|
||||
|
||||
// int pixel_stride = stride / 4;
|
||||
|
||||
// for (size_t y = 0; y < preview_height; ++y) {
|
||||
// for (size_t x = 0; x < preview_width; ++x) {
|
||||
// uint32_t p = data[x + y * pixel_stride];
|
||||
// pixels[x + y * preview_width] = p;
|
||||
// // uint16_t p = data[x + y * stride];
|
||||
// // uint32_t r = (p & 0b11111);
|
||||
// // uint32_t g = ((p >> 5) & 0b11111);
|
||||
// // uint32_t b = ((p >> 10) & 0b11111);
|
||||
// // pixels[x + y * preview_width] = (r << 16) | (g << 8) | b;
|
||||
// }
|
||||
// // memcpy(pixels + preview_width * y, data + stride * y, preview_width * sizeof(uint32_t));
|
||||
// }
|
||||
|
||||
// {
|
||||
// FILE *f = fopen("test.raw", "w");
|
||||
// fwrite(data, fsize, 1, f);
|
||||
// fclose(f);
|
||||
// }
|
||||
|
||||
// // memcpy(pixels, data, size);
|
||||
// munmap(data, size);
|
||||
// close(dmabuf_fd);
|
||||
// }
|
||||
glReadPixels(0, 0, preview_width, preview_height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
check_gl();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
#ifdef RENDERDOC
|
||||
if(rdoc_api) rdoc_api->EndFrameCapture(NULL, NULL);
|
||||
#endif
|
||||
} else {
|
||||
uint32_t surface_width, surface_height, skip;
|
||||
quick_preview_size(&surface_width, &surface_height, &skip, preview_width,
|
||||
preview_height, mode.width, mode.height,
|
||||
mode.pixel_format, camera->rotate);
|
||||
|
||||
surface = cairo_image_surface_create(
|
||||
CAIRO_FORMAT_RGB24, surface_width, surface_height);
|
||||
|
||||
uint8_t *pixels = cairo_image_surface_get_data(surface);
|
||||
|
||||
quick_preview((uint32_t *)pixels, surface_width, surface_height, image,
|
||||
mode.width, mode.height, mode.pixel_format,
|
||||
camera->rotate, camera->mirrored,
|
||||
camera->previewmatrix[0] == 0 ? NULL : camera->previewmatrix,
|
||||
camera->blacklevel, skip);
|
||||
// Pick an available buffer
|
||||
MPProcessPipelineBuffer *output_buffer = NULL;
|
||||
for (size_t i = 0; i < NUM_BUFFERS; ++i) {
|
||||
if (output_buffers[i].refcount == 0) {
|
||||
output_buffer = &output_buffers[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (output_buffer == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
assert(output_buffer != NULL);
|
||||
|
||||
#ifdef RENDERDOC
|
||||
if (rdoc_api) rdoc_api->StartFrameCapture(NULL, NULL);
|
||||
#endif
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
check_gl();
|
||||
|
||||
// Copy image to a GL texture. TODO: This can be avoided
|
||||
GLuint input_texture;
|
||||
glGenTextures(1, &input_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, input_texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, mode.width, mode.height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image);
|
||||
check_gl();
|
||||
|
||||
|
||||
gl_quick_preview(
|
||||
gl_quick_preview_state,
|
||||
output_buffer->texture_id, output_buffer_width, output_buffer_height,
|
||||
input_texture, mode.width, mode.height,
|
||||
mode.pixel_format,
|
||||
camera->rotate, camera->mirrored,
|
||||
camera->previewmatrix[0] == 0 ? NULL : camera->previewmatrix,
|
||||
camera->blacklevel);
|
||||
check_gl();
|
||||
|
||||
// surface = cairo_image_surface_create(
|
||||
// CAIRO_FORMAT_RGB24, preview_width, preview_height);
|
||||
// uint32_t *pixels = (uint32_t *)cairo_image_surface_get_data(surface);
|
||||
glFinish();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
clock_t t2 = clock();
|
||||
printf("%fms\n", (float)(t2 - t1) / CLOCKS_PER_SEC * 1000);
|
||||
|
||||
// {
|
||||
// glBindTexture(GL_TEXTURE_2D, textures[1]);
|
||||
// EGLImage egl_image = eglCreateImage(egl_display, egl_context, EGL_GL_TEXTURE_2D, (EGLClientBuffer)(size_t)textures[1], NULL);
|
||||
|
||||
// // Make sure it's in the expected format
|
||||
// int fourcc;
|
||||
// eglExportDMABUFImageQueryMESA(egl_display, egl_image, &fourcc, NULL, NULL);
|
||||
// assert(fourcc == DRM_FORMAT_ABGR8888);
|
||||
|
||||
|
||||
// int dmabuf_fd;
|
||||
// int stride, offset;
|
||||
// eglExportDMABUFImageMESA(egl_display, egl_image, &dmabuf_fd, &stride, &offset);
|
||||
|
||||
// int fsize = lseek(dmabuf_fd, 0, SEEK_END);
|
||||
// printf("SIZE %d STRIDE %d OFFSET %d SIZE %d:%d\n", fsize, stride, offset, preview_width, preview_height);
|
||||
|
||||
// size_t size = stride * preview_height;
|
||||
// uint32_t *data = mmap(NULL, fsize, PROT_READ, MAP_SHARED, dmabuf_fd, 0);
|
||||
// assert(data != MAP_FAILED);
|
||||
|
||||
// int pixel_stride = stride / 4;
|
||||
|
||||
// for (size_t y = 0; y < preview_height; ++y) {
|
||||
// for (size_t x = 0; x < preview_width; ++x) {
|
||||
// uint32_t p = data[x + y * pixel_stride];
|
||||
// pixels[x + y * preview_width] = p;
|
||||
// // uint16_t p = data[x + y * stride];
|
||||
// // uint32_t r = (p & 0b11111);
|
||||
// // uint32_t g = ((p >> 5) & 0b11111);
|
||||
// // uint32_t b = ((p >> 10) & 0b11111);
|
||||
// // pixels[x + y * preview_width] = (r << 16) | (g << 8) | b;
|
||||
// }
|
||||
// // memcpy(pixels + preview_width * y, data + stride * y, preview_width * sizeof(uint32_t));
|
||||
// }
|
||||
|
||||
// {
|
||||
// FILE *f = fopen("test.raw", "w");
|
||||
// fwrite(data, fsize, 1, f);
|
||||
// fclose(f);
|
||||
// }
|
||||
|
||||
// // memcpy(pixels, data, size);
|
||||
// munmap(data, size);
|
||||
// close(dmabuf_fd);
|
||||
// }
|
||||
// glReadPixels(0, 0, preview_width, preview_height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
// check_gl();
|
||||
|
||||
#ifdef RENDERDOC
|
||||
if(rdoc_api) rdoc_api->EndFrameCapture(NULL, NULL);
|
||||
#endif
|
||||
|
||||
mp_process_pipeline_buffer_ref(output_buffer);
|
||||
mp_main_set_preview(output_buffer);
|
||||
|
||||
// Create a thumbnail from the preview for the last capture
|
||||
cairo_surface_t *thumb = NULL;
|
||||
if (captures_remaining == 1) {
|
||||
printf("Making thumbnail\n");
|
||||
thumb = cairo_image_surface_create(
|
||||
CAIRO_FORMAT_ARGB32, MP_MAIN_THUMB_SIZE, MP_MAIN_THUMB_SIZE);
|
||||
// if (captures_remaining == 1) {
|
||||
// printf("Making thumbnail\n");
|
||||
// thumb = cairo_image_surface_create(
|
||||
// CAIRO_FORMAT_ARGB32, MP_MAIN_THUMB_SIZE, MP_MAIN_THUMB_SIZE);
|
||||
|
||||
cairo_t *cr = cairo_create(thumb);
|
||||
draw_surface_scaled_centered(
|
||||
cr, MP_MAIN_THUMB_SIZE, MP_MAIN_THUMB_SIZE, surface);
|
||||
cairo_destroy(cr);
|
||||
}
|
||||
// cairo_t *cr = cairo_create(thumb);
|
||||
// draw_surface_scaled_centered(
|
||||
// cr, MP_MAIN_THUMB_SIZE, MP_MAIN_THUMB_SIZE, surface);
|
||||
// cairo_destroy(cr);
|
||||
// }
|
||||
|
||||
// Pass processed preview to main and zbar
|
||||
mp_zbar_pipeline_process_image(cairo_surface_reference(surface));
|
||||
mp_main_set_preview(surface);
|
||||
// mp_zbar_pipeline_process_image(cairo_surface_reference(surface));
|
||||
|
||||
return thumb;
|
||||
}
|
||||
|
@ -755,17 +726,39 @@ mp_process_pipeline_capture()
|
|||
mp_pipeline_invoke(pipeline, capture, NULL, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
update_output_buffers()
|
||||
{
|
||||
output_buffer_width = mode.width / 2;
|
||||
output_buffer_height = mode.height / 2;
|
||||
|
||||
if (camera->rotate != 0 || camera->rotate != 180) {
|
||||
int tmp = output_buffer_width;
|
||||
output_buffer_width = output_buffer_height;
|
||||
output_buffer_height = tmp;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < NUM_BUFFERS; ++i) {
|
||||
glBindTexture(GL_TEXTURE_2D, output_buffers[i].texture_id);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, output_buffer_width, output_buffer_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state)
|
||||
{
|
||||
const bool buffer_update_required = (!mp_camera_mode_is_equivalent(&mode, &state->mode) || preview_width != state->preview_width || preview_height != state->preview_height);
|
||||
|
||||
camera = state->camera;
|
||||
mode = state->mode;
|
||||
|
||||
burst_length = state->burst_length;
|
||||
|
||||
preview_width = state->preview_width;
|
||||
preview_height = state->preview_height;
|
||||
|
||||
burst_length = state->burst_length;
|
||||
|
||||
// gain_is_manual = state->gain_is_manual;
|
||||
gain = state->gain;
|
||||
gain_max = state->gain_max;
|
||||
|
@ -773,6 +766,10 @@ update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state
|
|||
exposure_is_manual = state->exposure_is_manual;
|
||||
exposure = state->exposure;
|
||||
|
||||
if (buffer_update_required) {
|
||||
update_output_buffers();
|
||||
}
|
||||
|
||||
struct mp_main_state main_state = {
|
||||
.camera = camera,
|
||||
.mode = mode,
|
||||
|
@ -793,3 +790,6 @@ mp_process_pipeline_update_state(const struct mp_process_pipeline_state *new_sta
|
|||
mp_pipeline_invoke(pipeline, (MPPipelineCallback)update_state, new_state,
|
||||
sizeof(struct mp_process_pipeline_state));
|
||||
}
|
||||
|
||||
// FUCK YOU GTK
|
||||
void pango_fc_font_get_languages() {}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include "camera_config.h"
|
||||
|
||||
typedef struct _GdkWindow GdkWindow;
|
||||
typedef struct _GdkSurface GdkSurface;
|
||||
|
||||
struct mp_process_pipeline_state {
|
||||
const struct mp_camera_config *camera;
|
||||
|
@ -28,8 +28,14 @@ void mp_process_pipeline_start();
|
|||
void mp_process_pipeline_stop();
|
||||
void mp_process_pipeline_sync();
|
||||
|
||||
void mp_process_pipeline_init_gl(GdkWindow *window);
|
||||
void mp_process_pipeline_init_gl(GdkSurface *window);
|
||||
|
||||
void mp_process_pipeline_process_image(MPBuffer buffer);
|
||||
void mp_process_pipeline_capture();
|
||||
void mp_process_pipeline_update_state(const struct mp_process_pipeline_state *state);
|
||||
|
||||
typedef struct _MPProcessPipelineBuffer MPProcessPipelineBuffer;
|
||||
|
||||
void mp_process_pipeline_buffer_ref(MPProcessPipelineBuffer *buf);
|
||||
void mp_process_pipeline_buffer_unref(MPProcessPipelineBuffer *buf);
|
||||
uint32_t mp_process_pipeline_buffer_get_texture_id(MPProcessPipelineBuffer *buf);
|
||||
|
|
Loading…
Reference in New Issue