Reorganization, replacing CPU debayering entirely

This commit is contained in:
Benjamin Schaaf 2021-04-18 23:13:56 +10:00
parent 337526e9b9
commit 91817b167a
48 changed files with 571 additions and 1657 deletions

View File

@ -1,592 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.37.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<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">False</property>
<property name="title" translatable="yes">Camera</property>
<property name="default-width">360</property>
<property name="default-height">640</property>
<child>
<object class="GtkStack" id="main_stack">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkBox" id="page_main">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkGLArea" id="preview">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<style>
<class name="black"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="control_box">
<property name="can-focus">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</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="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-end">10</property>
<property name="label" translatable="yes">ISO</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScale" id="control_slider">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="adjustment">control_adj</property>
<property name="round-digits">1</property>
<property name="draw-value">False</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="control_auto">
<property name="label" translatable="yes">Auto</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="margin-start">10</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<style>
<class name="controlbox"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="controls_box">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child type="center">
<object class="GtkButton" id="shutter">
<property name="width-request">48</property>
<property name="height-request">48</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="always-show-image">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="resource">/org/postmarketos/Megapixels/shutter-button.svg</property>
</object>
</child>
<style>
<class name="circular"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-top">8</property>
<property name="margin-bottom">8</property>
<property name="spacing">10</property>
<child>
<object class="GtkButton" id="settings">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="resource">/org/postmarketos/Megapixels/settings-symbolic.svg</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="switch_camera">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="resource">/org/postmarketos/Megapixels/switch-camera.svg</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="padding">10</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-top">8</property>
<property name="margin-bottom">8</property>
<property name="spacing">10</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkButton" id="open_directory">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="resource">/org/postmarketos/Megapixels/folder-symbolic.svg</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="open_last">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<child>
<object class="GtkStack" id="open_last_stack">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkImage" id="thumb_last">
<property name="width-request">24</property>
<property name="height-request">24</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
</child>
<child>
<object class="GtkSpinner" id="process_spinner">
<property name="width-request">24</property>
<property name="height-request">24</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="padding">10</property>
<property name="pack-type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">10</property>
<property name="pack-type">end</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="error_box">
<property name="can-focus">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</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="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">No error</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="error_close">
<property name="label">gtk-close</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="use-stock">True</property>
<property name="always-show-image">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<style>
<class name="errorbox"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="name">main</property>
<property name="title" translatable="yes">page0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="page_settings">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="shadow-type">none</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</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="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkButton" id="settings_back">
<property name="label" translatable="yes">Back</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="margin-start">10</property>
<property name="margin-end">10</property>
<style>
<class name="suggested-action"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Settings aren't functional yet</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">4</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Photos</property>
<style>
<class name="heading"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label-xalign">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="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Resolution</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkComboBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Storage mode</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkRadioButton" id="store_vng">
<property name="label" translatable="yes">Debayer with VNG (slowest)</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="active">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="store_simple">
<property name="label" translatable="yes">Debayer with linear interpolation</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="draw-indicator">True</property>
<property name="group">store_vng</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="store_raw">
<property name="label" translatable="yes">Raw</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="draw-indicator">True</property>
<property name="group">store_vng</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
</object>
</child>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
<style>
<class name="view"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="name">settings</property>
<property name="title" translatable="yes">page1</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View File

@ -1,4 +1,6 @@
#ifdef GL_ES
precision mediump float;
#endif
uniform sampler2D texture;

View File

@ -1,4 +1,6 @@
#ifdef GL_ES
precision mediump float;
#endif
attribute vec2 vert;
attribute vec2 tex_coord;

View File

@ -25,6 +25,7 @@
<object class="GtkGLArea" id="preview">
<property name="vexpand">1</property>
<property name="can-focus">0</property>
<property name="use-es">1</property>
</object>
</child>
<child>

View File

@ -1,37 +1,28 @@
precision highp float;
#ifdef GL_ES
precision mediump float;
#endif
uniform sampler2D texture;
uniform sampler2D pixel_layout;
uniform mat3 color_matrix;
uniform vec2 pixel_size;
// varying vec2 uv1;
// varying vec2 uv2;
varying vec2 top_left_uv;
varying vec2 top_right_uv;
varying vec2 bottom_left_uv;
varying vec2 bottom_right_uv;
varying vec2 half_pixel_coord;
#define fetch(p) texture2D(texture, p).r
void main() {
vec4 pixels = vec4(
fetch(top_left_uv),
fetch(top_right_uv),
fetch(bottom_right_uv),
fetch(bottom_left_uv));
// Note the coordinates for texture samples need to be a varying, as the
// Mali-400 has this as a fast path allowing 32-bit floats. Otherwise
// they end up as 16-bit floats and that's not accurate enough.
vec4 samples = vec4(
texture2D(texture, top_left_uv).r,
texture2D(texture, top_right_uv).r,
texture2D(texture, bottom_left_uv).r,
texture2D(texture, bottom_right_uv).r);
vec2 arrangement = floor(half_pixel_coord);
vec2 is_bottom_left = step(1.0, arrangement);
// vec3 color = mix(
// mix(pixels.xyz, pixels.yzw, is_bottom_left.y),
// mix(pixels.wzy, pixels.zyx, is_bottom_left.y),
// is_bottom_left.x);
vec3 color = pixels.zyx;
// Assume BGGR for now. Currently this just takes 3 of the four samples
// for each pixel, there's room here to do some better debayering.
vec3 color = samples.wyx;
// Fast SRGB estimate. See https://mimosa-pudica.net/fast-gamma/
vec3 srgb_color = (vec3(1.138) * inversesqrt(color) - vec3(0.138)) * color;

View File

@ -1,20 +1,17 @@
precision highp float;
#ifdef GL_ES
precision mediump float;
#endif
attribute vec2 vert;
attribute vec2 tex_coord;
uniform mat3 transform;
uniform vec2 pixel_size;
uniform vec2 half_image_size;
// varying vec2 uv1;
// varying vec2 uv2;
varying vec2 top_left_uv;
varying vec2 top_right_uv;
varying vec2 bottom_left_uv;
varying vec2 bottom_right_uv;
varying vec2 half_pixel_coord;
void main() {
top_left_uv = tex_coord - pixel_size / 2.0;
@ -22,11 +19,5 @@ void main() {
top_right_uv = vec2(top_left_uv.x, bottom_right_uv.y);
bottom_left_uv = vec2(bottom_right_uv.x, top_left_uv.y);
// uv1 = tex_coord - pixel_size / 2.0;
// uv2 = tex_coord + pixel_size / 2.0;
// uv1 += pixel_size;
// uv2 += pixel_size;
half_pixel_coord = top_left_uv * half_image_size;
gl_Position = vec4(transform * vec3(vert, 1), 1);
}

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

15
data/meson.build Normal file
View File

@ -0,0 +1,15 @@
resources = gnome.compile_resources('megapixels-resources',
'org.postmarketos.Megapixels.gresource.xml')
install_data(['org.postmarketos.Megapixels.desktop'],
install_dir: get_option('datadir') / 'applications')
install_data(['org.postmarketos.Megapixels.metainfo.xml'],
install_dir: get_option('datadir') / 'metainfo')
install_data('org.postmarketos.Megapixels.svg',
install_dir: join_paths(get_option('datadir'), 'icons/hicolor/scalable/apps'))
install_data(['postprocess.sh'],
install_dir: get_option('datadir') / 'megapixels/',
install_mode: 'rwxr-xr-x')

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/postmarketos/Megapixels">
<file preprocess="xml-stripblanks">camera.ui</file>
<file>camera.css</file>
<file preprocess="xml-stripblanks">switch-camera.svg</file>
<file preprocess="xml-stripblanks">shutter-button.svg</file>
<file preprocess="xml-stripblanks">folder-symbolic.svg</file>
<file preprocess="xml-stripblanks">settings-symbolic.svg</file>
<file>blit.vert</file>
<file>blit.frag</file>
<file>debayer.vert</file>
<file>debayer.frag</file>
</gresource>
</gresources>

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,207 +0,0 @@
#include "camera.h"
#include "gl_quickpreview.h"
#include "gl_utils.h"
#include <stdlib.h>
#define VERTEX_ATTRIBUTE 0
#define TEX_COORD_ATTRIBUTE 1
struct _GLQuickPreview {
GLuint frame_buffer;
GLuint program;
GLuint uniform_transform;
GLuint uniform_pixel_size;
GLuint uniform_half_image_size;
GLuint uniform_texture;
GLuint uniform_pixel_layout;
GLuint uniform_srgb_map;
GLuint uniform_color_matrix;
GLuint pixel_layout_texture_id;
uint32_t pixel_layout_texture_width;
uint32_t pixel_layout_texture_height;
MPPixelFormat pixel_layout_texture_format;
};
GLQuickPreview *gl_quick_preview_new()
{
GLuint frame_buffer;
glGenFramebuffers(1, &frame_buffer);
check_gl();
GLuint shaders[] = {
gl_load_shader("data/debayer.vert", GL_VERTEX_SHADER),
gl_load_shader("data/debayer.frag", GL_FRAGMENT_SHADER),
};
GLuint program = gl_link_program(shaders, 2);
glBindAttribLocation(program, VERTEX_ATTRIBUTE, "vert");
glBindAttribLocation(program, TEX_COORD_ATTRIBUTE, "tex_coord");
check_gl();
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
check_gl();
GLuint pixel_layout_texture_id;
glGenTextures(1, &pixel_layout_texture_id);
check_gl();
GLQuickPreview *self = malloc(sizeof(GLQuickPreview));
self->frame_buffer = frame_buffer;
self->program = program;
self->uniform_transform = glGetUniformLocation(self->program, "transform");
self->uniform_pixel_size = glGetUniformLocation(self->program, "pixel_size");
self->uniform_half_image_size = glGetUniformLocation(self->program, "half_image_size");
self->uniform_texture = glGetUniformLocation(self->program, "texture");
self->uniform_pixel_layout = glGetUniformLocation(self->program, "pixel_layout");
self->uniform_color_matrix = glGetUniformLocation(self->program, "color_matrix");
self->uniform_srgb_map = glGetUniformLocation(self->program, "srgb_map");
check_gl();
self->pixel_layout_texture_id = pixel_layout_texture_id;
self->pixel_layout_texture_width = 0;
self->pixel_layout_texture_height = 0;
self->pixel_layout_texture_format = MP_PIXEL_FMT_BGGR8;
return self;
}
void gl_quick_preview_free(GLQuickPreview *self)
{
glDeleteFramebuffers(1, &self->frame_buffer);
free(self);
}
bool
ql_quick_preview_supports_format(GLQuickPreview *self, const MPPixelFormat format)
{
return format == MP_PIXEL_FMT_BGGR8;
}
bool
gl_quick_preview(GLQuickPreview *self,
GLuint dst_id,
const uint32_t dst_width, const uint32_t dst_height,
GLuint source_id,
const uint32_t src_width, const uint32_t src_height,
const MPPixelFormat format,
const uint32_t rotation,
const bool mirrored,
const float *colormatrix,
const uint8_t blacklevel)
{
// Generate pixel layout image
if (src_width != self->pixel_layout_texture_width
|| src_height != self->pixel_layout_texture_height
|| format != self->pixel_layout_texture_format) {
glBindTexture(GL_TEXTURE_2D, self->pixel_layout_texture_id);
uint16_t *buffer = malloc(src_width * src_height * sizeof(uint16_t));
for (size_t y = 0; y < src_height; ++y) {
for (size_t x = 0; x < src_width; ++x) {
buffer[x + y * src_width] =
(y % 2) == 0
? ((x % 2) == 0 ? 0b1111 << 4 : 0b1111 << 8)
: ((x % 2) == 0 ? 0b1111 << 8 : 0b1111 << 12);
}
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_width, src_height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, buffer);
check_gl();
free(buffer);
self->pixel_layout_texture_width = src_width;
self->pixel_layout_texture_height = src_height;
self->pixel_layout_texture_format = format;
glBindTexture(GL_TEXTURE_2D, 0);
check_gl();
}
assert(ql_quick_preview_supports_format(self, format));
glBindFramebuffer(GL_FRAMEBUFFER, self->frame_buffer);
glBindTexture(GL_TEXTURE_2D, dst_id);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_id, 0);
check_gl();
glViewport(0, 0, dst_width, dst_height);
check_gl();
assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
glUseProgram(self->program);
check_gl();
GLfloat rotation_list[4] = { 0, -1, 0, 1 };
int rotation_index = 4 - rotation / 90;
GLfloat sin_rot = rotation_list[rotation_index];
GLfloat cos_rot = rotation_list[(rotation_index + 1) % 4];
GLfloat scale_x = mirrored ? 1 : -1;
GLfloat matrix[9] = {
cos_rot * scale_x, sin_rot, 0,
-sin_rot * scale_x, cos_rot, 0,
0, 0, 1,
};
glUniformMatrix3fv(self->uniform_transform, 1, GL_FALSE, matrix);
check_gl();
glUniform2f(self->uniform_half_image_size, src_width / 2, src_height / 2);
check_gl();
GLfloat pixel_size_x = 1.0f / src_width;
GLfloat pixel_size_y = 1.0f / src_height;
glUniform2f(self->uniform_pixel_size, pixel_size_x, pixel_size_y);
check_gl();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, source_id);
glUniform1i(self->uniform_texture, 0);
check_gl();
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, self->pixel_layout_texture_id);
glUniform1i(self->uniform_pixel_layout, 1);
check_gl();
if (colormatrix)
{
GLfloat transposed[9];
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
transposed[i + j * 3] = colormatrix[j + i * 3];
glUniformMatrix3fv(self->uniform_color_matrix, 1, GL_FALSE, transposed);
}
else
{
static const GLfloat identity[9] = {
1, 0, 0,
0, 1, 0,
0, 0, 1,
};
glUniformMatrix3fv(self->uniform_color_matrix, 1, GL_FALSE, identity);
}
glVertexAttribPointer(VERTEX_ATTRIBUTE, 2, GL_FLOAT, 0, 0, gl_quad_vertices);
glEnableVertexAttribArray(VERTEX_ATTRIBUTE);
glVertexAttribPointer(TEX_COORD_ATTRIBUTE, 2, GL_FLOAT, 0, 0, gl_quad_texcoords);
glEnableVertexAttribArray(TEX_COORD_ATTRIBUTE);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
check_gl();
// TODO: Render
// glClearColor(0, 0, 1, 0);
// glClear(GL_COLOR_BUFFER_BIT);
// glBindTexture(GL_TEXTURE_2D, 0);
// glBindFramebuffer(GL_FRAMEBUFFER, 0);
return true;
}

View File

@ -1,21 +0,0 @@
#include "quickpreview.h"
#include <assert.h>
#include <stdio.h>
#include <GLES2/gl2.h>
typedef struct _GLQuickPreview GLQuickPreview;
GLQuickPreview* gl_quick_preview_new();
void gl_quick_preview_free(GLQuickPreview *self);
bool ql_quick_preview_supports_format(GLQuickPreview *self, const MPPixelFormat format);
bool gl_quick_preview(GLQuickPreview *self,
GLuint dst_id,
const uint32_t dst_width, const uint32_t dst_height,
GLuint source_id,
const uint32_t src_width, const uint32_t src_height,
const MPPixelFormat format,
const uint32_t rotation,
const bool mirrored,
const float *colormatrix,
const uint8_t blacklevel);

View File

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

View File

@ -1,78 +1,76 @@
project('megapixels', 'c')
gnome = import('gnome')
gtkdep = dependency('gtk4')
tiff = dependency('libtiff-4')
zbar = dependency('zbar')
threads = dependency('threads')
gl = dependency('gl')
egl = dependency('egl')
# gl = dependency('gl')
epoxy = dependency('epoxy')
cc = meson.get_compiler('c')
libm = cc.find_library('m', required: false)
resources = gnome.compile_resources('megapixels-resources', 'org.postmarketos.Megapixels.gresource.xml')
subdir('data')
conf = configuration_data()
conf.set_quoted('DATADIR', join_paths(get_option('prefix'), get_option('datadir')))
conf.set_quoted('SYSCONFDIR', get_option('sysconfdir'))
configure_file(
output: 'config.h',
configuration: conf )
configuration: conf)
# Define DEBUG for debug builds only (debugoptimized is not included on this one)
if get_option('buildtype') == 'debug'
add_global_arguments('-DDEBUG', language: 'c')
endif
# Workaround for libtiff having ABI changes but not changing the internal version number
# Workaround for libtiff having ABI changes but not changing the internal
# version number
if get_option('tiffcfapattern')
add_global_arguments('-DLIBTIFF_CFA_PATTERN', language: 'c')
endif
executable('megapixels',
'main.c',
'ini.c',
'quickpreview.c',
'gl_quickpreview.c',
'gl_utils.c',
'camera.c',
'device.c',
'pipeline.c',
'camera_config.c',
'io_pipeline.c',
'process_pipeline.c',
'zbar_pipeline.c',
'matrix.c',
resources,
dependencies : [gtkdep, libm, tiff, zbar, threads, gl, egl],
install : true,
link_args : '-Wl,-ldl')
'src/main.c',
'src/ini.c',
'src/gles2_debayer.c',
'src/gl_util.c',
'src/camera.c',
'src/device.c',
'src/pipeline.c',
'src/camera_config.c',
'src/io_pipeline.c',
'src/process_pipeline.c',
'src/zbar_pipeline.c',
'src/matrix.c',
resources,
include_directories: 'src/',
dependencies: [gtkdep, libm, tiff, zbar, threads, epoxy],
install: true,
link_args: '-Wl,-ldl')
install_data(['data/org.postmarketos.Megapixels.desktop'],
install_dir : get_option('datadir') / 'applications')
install_data(['data/org.postmarketos.Megapixels.metainfo.xml'],
install_dir : get_option('datadir') / 'metainfo')
install_data('data/org.postmarketos.Megapixels.svg',
install_dir: join_paths(get_option('datadir'), 'icons/hicolor/scalable/apps')
)
install_data([
'config/pine64,pinephone-1.0.ini',
'config/pine64,pinephone-1.1.ini',
'config/pine64,pinephone-1.2.ini',
'config/pine64,pinetab.ini',
install_data(
[
'config/pine64,pinephone-1.0.ini',
'config/pine64,pinephone-1.1.ini',
'config/pine64,pinephone-1.2.ini',
'config/pine64,pinetab.ini',
],
install_dir : get_option('datadir') / 'megapixels/config/')
install_data(['postprocess.sh'],
install_dir : get_option('datadir') / 'megapixels/',
install_mode: 'rwxr-xr-x')
install_dir: get_option('datadir') / 'megapixels/config/')
# Tools
executable('megapixels-list-devices', 'tools/list_devices.c', 'device.c', dependencies: [gtkdep], install: true)
executable('megapixels-camera-test', 'tools/camera_test.c', 'camera.c', 'device.c', dependencies: [gtkdep], install: true)
executable('megapixels-list-devices',
'tools/list_devices.c',
'src/device.c',
include_directories: 'src/',
dependencies: [gtkdep],
install: true)
test_quickpreview = executable('test_quickpreview', 'tests/test_quickpreview.c', 'quickpreview.c', 'camera.c', dependencies: [gtkdep])
test('quickpreview', test_quickpreview)
executable('megapixels-camera-test',
'tools/camera_test.c',
'src/camera.c',
'src/device.c',
include_directories: 'src/',
dependencies: [gtkdep],
install: true)

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/postmarketos/Megapixels">
<file>camera.glade</file>
<file>camera.css</file>
<file>switch-camera.svg</file>
<file>shutter-button.svg</file>
<file>folder-symbolic.svg</file>
<file>settings-symbolic.svg</file>
</gresource>
</gresources>

View File

@ -1,351 +0,0 @@
/*
* Fast but bad debayer method that scales and rotates by skipping source
* pixels and doesn't interpolate any values at all
*/
#include "quickpreview.h"
#include <assert.h>
#include <stdio.h>
/* Linear -> sRGB lookup table */
static const uint8_t srgb[] = {
0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70,
73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, 96, 97, 99, 100,
102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118,
120, 121, 122, 123, 124, 125, 126, 127, 129, 130, 131, 132, 133, 134,
135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147,
148, 149, 150, 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159,
160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, 170,
171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180,
181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189,
190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198,
199, 199, 200, 201, 201, 202, 202, 203, 204, 204, 205, 205, 206, 206,
207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214,
215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222,
222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, 228, 229, 229,
230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236,
237, 237, 237, 238, 238, 239, 239, 240, 240, 241, 241, 242, 242, 243,
243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249,
250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255
};
static inline uint32_t
pack_rgb(uint8_t r, uint8_t g, uint8_t b)
{
return (r << 16) | (g << 8) | b;
}
static inline uint32_t
convert_yuv_to_srgb(uint8_t y, uint8_t u, uint8_t v)
{
uint32_t r = 1.164f * y + 1.596f * (v - 128);
uint32_t g = 1.164f * y - 0.813f * (v - 128) - 0.391f * (u - 128);
uint32_t b = 1.164f * y + 2.018f * (u - 128);
return pack_rgb(r, g, b);
}
static inline uint32_t
apply_colormatrix(uint32_t color, const float *colormatrix)
{
if (!colormatrix) {
return color;
}
uint32_t r = (color >> 16) * colormatrix[0] +
((color >> 8) & 0xFF) * colormatrix[1] +
(color & 0xFF) * colormatrix[2];
uint32_t g = (color >> 16) * colormatrix[3] +
((color >> 8) & 0xFF) * colormatrix[4] +
(color & 0xFF) * colormatrix[5];
uint32_t b = (color >> 16) * colormatrix[6] +
((color >> 8) & 0xFF) * colormatrix[7] +
(color & 0xFF) * colormatrix[8];
// Clip colors
if (r > 0xFF)
r = 0xFF;
if (g > 0xFF)
g = 0xFF;
if (b > 0xFF)
b = 0xFF;
return pack_rgb(r, g, b);
}
static inline uint32_t
coord_map(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int rotation,
bool mirrored)
{
uint32_t x_r, y_r;
if (rotation == 0) {
x_r = x;
y_r = y;
} else if (rotation == 90) {
x_r = y;
y_r = height - x - 1;
} else if (rotation == 270) {
x_r = width - y - 1;
y_r = x;
} else {
x_r = width - x - 1;
y_r = height - y - 1;
}
if (mirrored) {
x_r = width - x_r - 1;
}
uint32_t index = y_r * width + x_r;
#ifdef DEBUG
assert(index < width * height);
#endif
return index;
}
static void
quick_preview_rggb8(uint32_t *dst, const uint32_t dst_width,
const uint32_t dst_height, const uint8_t *src,
const uint32_t src_width, const uint32_t src_height,
const MPPixelFormat format, const uint32_t rotation,
const bool mirrored, const float *colormatrix,
const uint8_t blacklevel, const uint32_t skip)
{
uint32_t src_y = 0, dst_y = 0;
while (src_y < src_height) {
uint32_t src_x = 0, dst_x = 0;
while (src_x < src_width) {
uint32_t src_i = src_y * src_width + src_x;
uint8_t b0 = srgb[src[src_i] - blacklevel];
uint8_t b1 = srgb[src[src_i + 1] - blacklevel];
uint8_t b2 = srgb[src[src_i + src_width + 1] - blacklevel];
uint32_t color;
switch (format) {
case MP_PIXEL_FMT_BGGR8:
color = pack_rgb(b2, b1, b0);
break;
case MP_PIXEL_FMT_GBRG8:
color = pack_rgb(b2, b0, b1);
break;
case MP_PIXEL_FMT_GRBG8:
color = pack_rgb(b1, b0, b2);
break;
case MP_PIXEL_FMT_RGGB8:
color = pack_rgb(b0, b1, b2);
break;
default:
assert(false);
}
color = apply_colormatrix(color, colormatrix);
dst[coord_map(dst_x, dst_y, dst_width, dst_height, rotation,
mirrored)] = color;
src_x += 2 + 2 * skip;
++dst_x;
}
src_y += 2 + 2 * skip;
++dst_y;
}
}
static void
quick_preview_rggb10(uint32_t *dst, const uint32_t dst_width,
const uint32_t dst_height, const uint8_t *src,
const uint32_t src_width, const uint32_t src_height,
const MPPixelFormat format, const uint32_t rotation,
const bool mirrored, const float *colormatrix,
const uint8_t blacklevel, const uint32_t skip)
{
assert(src_width % 2 == 0);
uint32_t width_bytes = mp_pixel_format_width_to_bytes(format, src_width);
uint32_t src_y = 0, dst_y = 0;
while (src_y < src_height) {
uint32_t src_x = 0, dst_x = 0;
while (src_x < width_bytes) {
uint32_t src_i = src_y * width_bytes + src_x;
uint8_t b0 = srgb[src[src_i] - blacklevel];
uint8_t b1 = srgb[src[src_i + 1] - blacklevel];
uint8_t b2 = srgb[src[src_i + width_bytes + 1] - blacklevel];
uint32_t color;
switch (format) {
case MP_PIXEL_FMT_BGGR10P:
color = pack_rgb(b2, b1, b0);
break;
case MP_PIXEL_FMT_GBRG10P:
color = pack_rgb(b2, b0, b1);
break;
case MP_PIXEL_FMT_GRBG10P:
color = pack_rgb(b1, b0, b2);
break;
case MP_PIXEL_FMT_RGGB10P:
color = pack_rgb(b0, b1, b2);
break;
default:
assert(false);
}
color = apply_colormatrix(color, colormatrix);
dst[coord_map(dst_x, dst_y, dst_width, dst_height, rotation,
mirrored)] = color;
uint32_t advance = 1 + skip;
if (src_x % 5 == 0) {
src_x += 2 * (advance % 2) + 5 * (advance / 2);
} else {
src_x += 3 * (advance % 2) + 5 * (advance / 2);
}
++dst_x;
}
src_y += 2 + 2 * skip;
++dst_y;
}
}
static void
quick_preview_yuv(uint32_t *dst, const uint32_t dst_width, const uint32_t dst_height,
const uint8_t *src, const uint32_t src_width,
const uint32_t src_height, const MPPixelFormat format,
const uint32_t rotation, const bool mirrored,
const float *colormatrix, const uint32_t skip)
{
assert(src_width % 2 == 0);
uint32_t width_bytes = src_width * 2;
uint32_t unrot_dst_width = dst_width;
if (rotation != 0 && rotation != 180) {
unrot_dst_width = dst_height;
}
uint32_t src_y = 0, dst_y = 0;
while (src_y < src_height) {
uint32_t src_x = 0, dst_x = 0;
while (src_x < width_bytes) {
uint32_t src_i = src_y * width_bytes + src_x;
uint8_t b0 = src[src_i];
uint8_t b1 = src[src_i + 1];
uint8_t b2 = src[src_i + 2];
uint8_t b3 = src[src_i + 3];
uint32_t color1, color2;
switch (format) {
case MP_PIXEL_FMT_UYVY:
color1 = convert_yuv_to_srgb(b1, b0, b2);
color2 = convert_yuv_to_srgb(b3, b0, b2);
break;
case MP_PIXEL_FMT_YUYV:
color1 = convert_yuv_to_srgb(b0, b1, b3);
color2 = convert_yuv_to_srgb(b2, b1, b3);
break;
default:
assert(false);
}
color1 = apply_colormatrix(color1, colormatrix);
color2 = apply_colormatrix(color2, colormatrix);
uint32_t dst_i1 = coord_map(dst_x, dst_y, dst_width,
dst_height, rotation, mirrored);
dst[dst_i1] = color1;
++dst_x;
// The last pixel needs to be skipped if we have an odd un-rotated width
if (dst_x < unrot_dst_width) {
uint32_t dst_i2 =
coord_map(dst_x, dst_y, dst_width,
dst_height, rotation, mirrored);
dst[dst_i2] = color2;
++dst_x;
}
src_x += 4 + 4 * skip;
}
src_y += 1 + skip;
++dst_y;
}
}
void
quick_preview(uint32_t *dst, const uint32_t dst_width, const uint32_t dst_height,
const uint8_t *src, const uint32_t src_width,
const uint32_t src_height, const MPPixelFormat format,
const uint32_t rotation, const bool mirrored, const float *colormatrix,
const uint8_t blacklevel, const uint32_t skip)
{
switch (format) {
case MP_PIXEL_FMT_BGGR8:
case MP_PIXEL_FMT_GBRG8:
case MP_PIXEL_FMT_GRBG8:
case MP_PIXEL_FMT_RGGB8:
quick_preview_rggb8(dst, dst_width, dst_height, src, src_width,
src_height, format, rotation, mirrored,
colormatrix, blacklevel, skip);
break;
case MP_PIXEL_FMT_BGGR10P:
case MP_PIXEL_FMT_GBRG10P:
case MP_PIXEL_FMT_GRBG10P:
case MP_PIXEL_FMT_RGGB10P:
quick_preview_rggb10(dst, dst_width, dst_height, src, src_width,
src_height, format, rotation, mirrored,
colormatrix, blacklevel, skip);
break;
case MP_PIXEL_FMT_UYVY:
case MP_PIXEL_FMT_YUYV:
quick_preview_yuv(dst, dst_width, dst_height, src, src_width,
src_height, format, rotation, mirrored,
colormatrix, skip);
break;
default:
assert(false);
}
}
static uint32_t
div_ceil(uint32_t x, uint32_t y)
{
return x / y + !!(x % y);
}
void
quick_preview_size(uint32_t *dst_width, uint32_t *dst_height, uint32_t *skip,
const uint32_t preview_width, const uint32_t preview_height,
const uint32_t src_width, const uint32_t src_height,
const MPPixelFormat format, const int rotation)
{
uint32_t colors_x = mp_pixel_format_width_to_colors(format, src_width);
uint32_t colors_y = mp_pixel_format_height_to_colors(format, src_height);
if (rotation != 0 && rotation != 180) {
uint32_t tmp = colors_x;
colors_x = colors_y;
colors_y = tmp;
}
uint32_t scale_x = colors_x / preview_width;
uint32_t scale_y = colors_y / preview_height;
if (scale_x > 0)
--scale_x;
if (scale_y > 0)
--scale_y;
*skip = scale_x > scale_y ? scale_x : scale_y;
*dst_width = div_ceil(colors_x, (1 + *skip));
if (*dst_width <= 0)
*dst_width = 1;
*dst_height = div_ceil(colors_y, (1 + *skip));
if (*dst_height <= 0)
*dst_height = 1;
}

View File

@ -1,14 +0,0 @@
#include "camera.h"
#include <stdint.h>
void quick_preview(uint32_t *dst, const uint32_t dst_width,
const uint32_t dst_height, const uint8_t *src,
const uint32_t src_width, const uint32_t src_height,
const MPPixelFormat format, const uint32_t rotation,
const bool mirrored, const float *colormatrix,
const uint8_t blacklevel, const uint32_t skip);
void quick_preview_size(uint32_t *dst_width, uint32_t *dst_height, uint32_t *skip,
const uint32_t preview_width, const uint32_t preview_height,
const uint32_t src_width, const uint32_t src_height,
const MPPixelFormat format, const int rotation);

220
src/gl_util.c Normal file
View File

@ -0,0 +1,220 @@
#include "gl_util.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <gio/gio.h>
#include <gmodule.h>
#include <gdk/gdk.h>
void gl_util_check_error(const char *file, int line)
{
GLenum error = glGetError();
const char *name;
switch (error) {
case GL_NO_ERROR:
return; // no error
case GL_INVALID_ENUM:
name = "GL_INVALID_ENUM";
break;
case GL_INVALID_VALUE:
name = "GL_INVALID_VALUE";
break;
case GL_INVALID_OPERATION:
name = "GL_INVALID_OPERATION";
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
name = "GL_INVALID_FRAMEBUFFER_OPERATION";
break;
case GL_OUT_OF_MEMORY:
name = "GL_OUT_OF_MEMORY";
break;
default:
name = "UNKNOWN ERROR!";
break;
}
printf("GL error at %s:%d - %s\n", file, line, name);
// raise(SIGTRAP);
}
GLuint
gl_util_load_shader(const char *resource, GLenum type, const char **extra_sources, size_t num_extra)
{
GdkGLContext *context = gdk_gl_context_get_current();
assert(context);
GLuint shader = glCreateShader(type);
if (shader == 0) {
return 0;
}
GBytes *bytes = g_resources_lookup_data(resource, 0, NULL);
if (!bytes) {
printf("Failed to load shader resource %s\n", resource);
return 0;
}
// Build #define for OpenGL context information
gboolean is_es = gdk_gl_context_get_use_es(context);
int major, minor;
gdk_gl_context_get_version(context, &major, &minor);
char context_info_buf[128];
snprintf(context_info_buf, 128, "#define %s\n#define GL_%d\n#define GL_%d_%d\n", is_es ? "GL_ES" : "GL_NO_ES", major, major, minor);
gsize glib_size = 0;
const GLchar *source = g_bytes_get_data(bytes, &glib_size);
if (glib_size == 0 || glib_size > INT_MAX) {
printf("Invalid size for resource\n");
return 0;
}
const GLchar **sources = malloc((num_extra + 1) * sizeof(GLchar *));
GLint *sizes = malloc((num_extra + 1) * sizeof(GLint));
for (size_t i = 0; i < num_extra; ++i) {
sources[i] = extra_sources[i];
sizes[i] = -1;
}
sources[num_extra] = source;
sizes[num_extra] = glib_size;
glShaderSource(shader, num_extra + 1, sources, sizes);
glCompileShader(shader);
check_gl();
free(sources);
free(sizes);
g_bytes_unref(bytes);
// Check compile status
GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (success == GL_FALSE) {
printf("Shader compilation failed for %s\n", resource);
glDeleteShader(shader);
return 0;
}
GLint log_length;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 0) {
char *log = malloc(sizeof(char) * log_length);
glGetShaderInfoLog(shader, log_length - 1, &log_length, log);
printf("Shader %s log: %s\n", resource, log);
free(log);
glDeleteShader(shader);
return 0;
}
return shader;
}
GLuint
gl_util_link_program(GLuint *shaders, size_t num_shaders)
{
GLuint program = glCreateProgram();
for (size_t i = 0; i < num_shaders; ++i) {
glAttachShader(program, shaders[i]);
}
glLinkProgram(program);
check_gl();
GLint success;
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (success == GL_FALSE) {
printf("Program linking failed\n");
}
GLint log_length;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 0) {
char *log = malloc(sizeof(char) * log_length);
glGetProgramInfoLog(program, log_length - 1, &log_length, log);
printf("Program log: %s\n", log);
free(log);
}
check_gl();
return program;
}
static const GLfloat quad_data[] = {
// Vertices
-1, -1,
1, -1,
-1, 1,
1, 1,
// Texcoords
0, 0,
1, 0,
0, 1,
1, 1,
};
GLuint gl_util_new_quad()
{
GdkGLContext *context = gdk_gl_context_get_current();
assert(context);
if (gdk_gl_context_get_use_es(context)) {
return 0;
} else {
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(quad_data), quad_data, GL_STATIC_DRAW);
check_gl();
glBindBuffer(GL_ARRAY_BUFFER, 0);
check_gl();
return buffer;
}
}
void gl_util_bind_quad(GLuint buffer)
{
GdkGLContext *context = gdk_gl_context_get_current();
assert(context);
if (gdk_gl_context_get_use_es(context)) {
glVertexAttribPointer(GL_UTIL_VERTEX_ATTRIBUTE, 2, GL_FLOAT, 0, 0, quad_data);
check_gl();
glEnableVertexAttribArray(GL_UTIL_VERTEX_ATTRIBUTE);
check_gl();
glVertexAttribPointer(GL_UTIL_TEX_COORD_ATTRIBUTE, 2, GL_FLOAT, 0, 0, quad_data + 8);
check_gl();
glEnableVertexAttribArray(GL_UTIL_TEX_COORD_ATTRIBUTE);
check_gl();
} else {
glBindBuffer(GL_ARRAY_BUFFER, buffer);
check_gl();
glVertexAttribPointer(GL_UTIL_VERTEX_ATTRIBUTE, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(GL_UTIL_VERTEX_ATTRIBUTE);
check_gl();
glVertexAttribPointer(GL_UTIL_TEX_COORD_ATTRIBUTE, 2, GL_FLOAT, GL_FALSE, 0, (void*) (8 * sizeof(float)));
glEnableVertexAttribArray(GL_UTIL_TEX_COORD_ATTRIBUTE);
check_gl();
}
}
void gl_util_draw_quad(GLuint buffer)
{
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
check_gl();
}

17
src/gl_util.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <epoxy/gl.h>
#include <stddef.h>
#define GL_UTIL_VERTEX_ATTRIBUTE 0
#define GL_UTIL_TEX_COORD_ATTRIBUTE 1
#define check_gl() gl_util_check_error(__FILE__, __LINE__)
void gl_util_check_error(const char *file, int line);
GLuint gl_util_load_shader(const char *resource, GLenum type, const char **extra_sources, size_t num_extra);
GLuint gl_util_link_program(GLuint *shaders, size_t num_shaders);
GLuint gl_util_new_quad();
void gl_util_bind_quad(GLuint buffer);
void gl_util_draw_quad(GLuint buffer);

141
src/gles2_debayer.c Normal file
View File

@ -0,0 +1,141 @@
#include "gles2_debayer.h"
#include "camera.h"
#include "gl_util.h"
#include <stdlib.h>
#define VERTEX_ATTRIBUTE 0
#define TEX_COORD_ATTRIBUTE 1
struct _GLES2Debayer {
GLuint frame_buffer;
GLuint program;
GLuint uniform_transform;
GLuint uniform_pixel_size;
GLuint uniform_texture;
GLuint uniform_color_matrix;
GLuint quad;
};
GLES2Debayer *
gles2_debayer_new(MPPixelFormat format)
{
if (format != MP_PIXEL_FMT_BGGR8) {
return NULL;
}
GLuint frame_buffer;
glGenFramebuffers(1, &frame_buffer);
check_gl();
GLuint shaders[] = {
gl_util_load_shader("/org/postmarketos/Megapixels/debayer.vert", GL_VERTEX_SHADER, NULL, 0),
gl_util_load_shader("/org/postmarketos/Megapixels/debayer.frag", GL_FRAGMENT_SHADER, NULL, 0),
};
GLuint program = gl_util_link_program(shaders, 2);
glBindAttribLocation(program, VERTEX_ATTRIBUTE, "vert");
glBindAttribLocation(program, TEX_COORD_ATTRIBUTE, "tex_coord");
check_gl();
GLES2Debayer *self = malloc(sizeof(GLES2Debayer));
self->frame_buffer = frame_buffer;
self->program = program;
self->uniform_transform = glGetUniformLocation(self->program, "transform");
self->uniform_pixel_size = glGetUniformLocation(self->program, "pixel_size");
self->uniform_texture = glGetUniformLocation(self->program, "texture");
self->uniform_color_matrix = glGetUniformLocation(self->program, "color_matrix");
check_gl();
self->quad = gl_util_new_quad();
return self;
}
void
gles2_debayer_free(GLES2Debayer *self)
{
glDeleteFramebuffers(1, &self->frame_buffer);
glDeleteProgram(self->program);
free(self);
}
void
gles2_debayer_use(GLES2Debayer *self)
{
glUseProgram(self->program);
check_gl();
gl_util_bind_quad(self->quad);
}
void
gles2_debayer_configure(GLES2Debayer *self,
const uint32_t dst_width, const uint32_t dst_height,
const uint32_t src_width, const uint32_t src_height,
const uint32_t rotation,
const bool mirrored,
const float *colormatrix,
const uint8_t blacklevel)
{
glViewport(0, 0, dst_width, dst_height);
check_gl();
GLfloat rotation_list[4] = { 0, -1, 0, 1 };
int rotation_index = 4 - rotation / 90;
GLfloat sin_rot = rotation_list[rotation_index];
GLfloat cos_rot = rotation_list[(rotation_index + 1) % 4];
GLfloat scale_x = mirrored ? 1 : -1;
GLfloat matrix[9] = {
cos_rot * scale_x, sin_rot, 0,
-sin_rot * scale_x, cos_rot, 0,
0, 0, 1,
};
glUniformMatrix3fv(self->uniform_transform, 1, GL_FALSE, matrix);
check_gl();
GLfloat pixel_size_x = 1.0f / src_width;
GLfloat pixel_size_y = 1.0f / src_height;
glUniform2f(self->uniform_pixel_size, pixel_size_x, pixel_size_y);
check_gl();
if (colormatrix) {
GLfloat transposed[9];
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
transposed[i + j * 3] = colormatrix[j + i * 3];
glUniformMatrix3fv(self->uniform_color_matrix, 1, GL_FALSE, transposed);
} else {
static const GLfloat identity[9] = {
1, 0, 0,
0, 1, 0,
0, 0, 1,
};
glUniformMatrix3fv(self->uniform_color_matrix, 1, GL_FALSE, identity);
}
check_gl();
}
void
gles2_debayer_process(GLES2Debayer *self, GLuint dst_id, GLuint source_id)
{
glBindFramebuffer(GL_FRAMEBUFFER, self->frame_buffer);
glBindTexture(GL_TEXTURE_2D, dst_id);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_id, 0);
check_gl();
assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, source_id);
glUniform1i(self->uniform_texture, 0);
check_gl();
gl_util_draw_quad(self->quad);
}

21
src/gles2_debayer.h Normal file
View File

@ -0,0 +1,21 @@
#include "camera.h"
#include "gl_util.h"
#include <assert.h>
#include <stdio.h>
typedef struct _GLES2Debayer GLES2Debayer;
GLES2Debayer* gles2_debayer_new(MPPixelFormat format);
void gles2_debayer_free(GLES2Debayer *self);
void gles2_debayer_use(GLES2Debayer *self);
void gles2_debayer_configure(GLES2Debayer *self,
const uint32_t dst_width, const uint32_t dst_height,
const uint32_t src_width, const uint32_t src_height,
const uint32_t rotation,
const bool mirrored,
const float *colormatrix,
const uint8_t blacklevel);
void gles2_debayer_process(GLES2Debayer *self, GLuint dst_id, GLuint source_id);

View File

View File

View File

@ -18,9 +18,8 @@
#include <gtk/gtk.h>
#include <locale.h>
#include <zbar.h>
#include "gl_utils.h"
#include "gl_util.h"
#include "camera_config.h"
#include "quickpreview.h"
#include "io_pipeline.h"
#include "process_pipeline.h"
@ -330,6 +329,7 @@ draw_controls()
static GLuint blit_program;
static GLuint blit_uniform_texture;
static GLuint quad;
static void
preview_realize(GtkGLArea *area)
@ -340,17 +340,27 @@ preview_realize(GtkGLArea *area)
return;
}
// Make a VAO for OpenGL
if (!gtk_gl_area_get_use_es(area)) {
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
check_gl();
}
GLuint blit_shaders[] = {
gl_load_shader("data/blit.vert", GL_VERTEX_SHADER),
gl_load_shader("data/blit.frag", GL_FRAGMENT_SHADER),
gl_util_load_shader("/org/postmarketos/Megapixels/blit.vert", GL_VERTEX_SHADER, NULL, 0),
gl_util_load_shader("/org/postmarketos/Megapixels/blit.frag", GL_FRAGMENT_SHADER, NULL, 0),
};
blit_program = gl_link_program(blit_shaders, 2);
glBindAttribLocation(blit_program, 0, "vert");
glBindAttribLocation(blit_program, 1, "tex_coord");
blit_program = gl_util_link_program(blit_shaders, 2);
glBindAttribLocation(blit_program, GL_UTIL_VERTEX_ATTRIBUTE, "vert");
glBindAttribLocation(blit_program, GL_UTIL_TEX_COORD_ATTRIBUTE, "tex_coord");
check_gl();
blit_uniform_texture = glGetUniformLocation(blit_program, "texture");
quad = gl_util_new_quad();
}
static gboolean
@ -364,9 +374,9 @@ preview_draw(GtkGLArea *area, GdkGLContext *ctx, gpointer data)
return FALSE;
}
// #ifdef RENDERDOC
// if (rdoc_api) rdoc_api->StartFrameCapture(NULL, NULL);
// #endif
#ifdef RENDERDOC
if (rdoc_api) rdoc_api->StartFrameCapture(NULL, NULL);
#endif
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
@ -377,13 +387,10 @@ preview_draw(GtkGLArea *area, GdkGLContext *ctx, gpointer data)
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mp_process_pipeline_buffer_get_texture_id(current_preview_buffer));
glUniform1i(blit_uniform_texture, 0);
glVertexAttribPointer(0, 2, GL_FLOAT, 0, 0, gl_quad_vertices);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, gl_quad_texcoords);
glEnableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
check_gl();
gl_util_bind_quad(quad);
gl_util_draw_quad(quad);
}
/*
@ -433,9 +440,9 @@ preview_draw(GtkGLArea *area, GdkGLContext *ctx, gpointer data)
glFlush();
// #ifdef RENDERDOC
// if(rdoc_api) rdoc_api->EndFrameCapture(NULL, NULL);
// #endif
#ifdef RENDERDOC
if(rdoc_api) rdoc_api->EndFrameCapture(NULL, NULL);
#endif
return FALSE;
}
@ -758,6 +765,9 @@ on_realize(GtkWidget *window, gpointer *data)
{
GtkNative *native = gtk_widget_get_native(window);
mp_process_pipeline_init_gl(gtk_native_get_surface(native));
camera = mp_get_camera_config(0);
update_io_pipeline();
}
typedef struct
@ -778,13 +788,10 @@ startup(GApplication *app)
g_object_set(gtk_settings_get_default(), "gtk-application-prefer-dark-theme",
TRUE, NULL);
GtkBuilder *builder;
if (access("camera.ui", F_OK) != -1) {
builder = gtk_builder_new_from_file("camera.ui");
} else {
builder = gtk_builder_new_from_file(
"/org/postmarketos/Megapixels/camera.ui");
}
assert(g_resources_lookup_data("/org/postmarketos/Megapixels/camera.ui", 0, NULL) != NULL);
GtkBuilder *builder = gtk_builder_new_from_resource(
"/org/postmarketos/Megapixels/camera.ui");
GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
GtkWidget *shutter = GTK_WIDGET(gtk_builder_get_object(builder, "shutter"));
@ -842,12 +849,8 @@ startup(GApplication *app)
G_CALLBACK(on_control_slider_changed), NULL);
GtkCssProvider *provider = gtk_css_provider_new();
if (access("camera.css", F_OK) != -1) {
gtk_css_provider_load_from_path(provider, "camera.css");
} else {
gtk_css_provider_load_from_resource(
provider, "/org/postmarketos/Megapixels/camera.css");
}
gtk_css_provider_load_from_resource(
provider, "/org/postmarketos/Megapixels/camera.css");
GtkStyleContext *context = gtk_widget_get_style_context(error_box);
gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
@ -868,9 +871,6 @@ startup(GApplication *app)
mp_io_pipeline_start();
camera = mp_get_camera_config(0);
update_io_pipeline();
gtk_application_add_window(GTK_APPLICATION(app), GTK_WINDOW(window));
gtk_widget_show(window);
}

View File

View File

@ -5,18 +5,14 @@
#include "io_pipeline.h"
#include "main.h"
#include "config.h"
#include "quickpreview.h"
#include "gl_quickpreview.h"
#include "gles2_debayer.h"
#include <tiffio.h>
#include <assert.h>
#include <math.h>
#include <wordexp.h>
#include <gtk/gtk.h>
#include "gl_utils.h"
#include <EGL/egl.h>
#include <EGL/eglext.h>
// #include <gdk/gdkwayland.h>
#include "gl_util.h"
#include <sys/mman.h>
#include <drm/drm_fourcc.h>
@ -82,9 +78,9 @@ find_processor(char *script)
wordfree(&exp_result);
// Check postprocess.h in the current working directory
sprintf(script, "%s", filename);
sprintf(script, "data/%s", filename);
if (access(script, F_OK) != -1) {
sprintf(script, "./%s", filename);
sprintf(script, "./data/%s", filename);
printf("Found postprocessor script at %s\n", script);
return true;
}
@ -158,9 +154,6 @@ struct _MPProcessPipelineBuffer {
};
static MPProcessPipelineBuffer output_buffers[NUM_BUFFERS];
static int output_buffer_width = 0;
static int output_buffer_height = 0;
void
mp_process_pipeline_buffer_ref(MPProcessPipelineBuffer *buf)
{
@ -179,37 +172,10 @@ mp_process_pipeline_buffer_get_texture_id(MPProcessPipelineBuffer *buf)
return buf->texture_id;
}
static GLQuickPreview *gl_quick_preview_state = NULL;
static GLES2Debayer *gles2_debayer = NULL;
static GdkGLContext *context;
static PFNEGLEXPORTDMABUFIMAGEMESAPROC eglExportDMABUFImageMESA;
static PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC eglExportDMABUFImageQueryMESA;
// static const char *
// egl_get_error_str()
// {
// EGLint error = eglGetError();
// switch (error) {
// case EGL_SUCCESS: return "EGL_SUCCESS";
// case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED";
// case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS";
// case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC";
// case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE";
// case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT";
// case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG";
// case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
// case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY";
// case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE";
// case EGL_BAD_MATCH: return "EGL_BAD_MATCH";
// case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER";
// case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
// case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
// case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST";
// }
// return "Unknown";
// }
#define RENDERDOC
#ifdef RENDERDOC
@ -230,6 +196,7 @@ init_gl(MPPipeline *pipeline, GdkSurface **surface)
gdk_gl_context_set_use_es(context, true);
gdk_gl_context_set_required_version(context, 2, 0);
gdk_gl_context_set_forward_compatible(context, false);
#ifdef DEBUG
gdk_gl_context_set_debug_enabled(context, true);
#else
@ -247,29 +214,22 @@ init_gl(MPPipeline *pipeline, GdkSurface **surface)
gdk_gl_context_make_current(context);
check_gl();
eglExportDMABUFImageMESA = (PFNEGLEXPORTDMABUFIMAGEMESAPROC)
eglGetProcAddress("eglExportDMABUFImageMESA");
eglExportDMABUFImageQueryMESA = (PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC)
eglGetProcAddress("eglExportDMABUFImageQueryMESA");
// Make a VAO for OpenGL
if (!gdk_gl_context_get_use_es(context)) {
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
check_gl();
}
// Generate textures for the buffers
// GLuint textures[NUM_BUFFERS * 2];
// glGenTextures(NUM_BUFFERS * 2, textures);
// for (size_t i = 0; i < NUM_BUFFERS; ++i) {
// input_buffers[i].texture_id = textures[i];
// input_buffers[i].egl_image = EGL_NO_IMAGE;
// input_buffers[i].dma_fd = -1;
// }
// for (size_t i = 0; i < NUM_BUFFERS; ++i) {
// output_buffers[i].texture_id = textures[NUM_BUFFERS + i];
// output_buffers[i].egl_image = EGL_NO_IMAGE;
// output_buffers[i].dma_fd = -1;
// }
gl_quick_preview_state = gl_quick_preview_new();
gles2_debayer = gles2_debayer_new(MP_PIXEL_FMT_BGGR8);
check_gl();
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
check_gl();
gles2_debayer_use(gles2_debayer);
for (size_t i = 0; i < NUM_BUFFERS; ++i) {
glGenTextures(1, &output_buffers[i].texture_id);
glBindTexture(GL_TEXTURE_2D, output_buffers[i].texture_id);
@ -279,7 +239,11 @@ init_gl(MPPipeline *pipeline, GdkSurface **surface)
glBindTexture(GL_TEXTURE_2D, 0);
printf("Initialized OpenGL\n");
gboolean is_es = gdk_gl_context_get_use_es(context);
int major, minor;
gdk_gl_context_get_version(context, &major, &minor);
printf("Initialized %s %d.%d\n", is_es ? "OpenGL ES" : "OpenGL", major, minor);
}
void
@ -293,8 +257,6 @@ process_image_for_preview(const uint8_t *image)
{
clock_t t1 = clock();
assert(gl_quick_preview_state && ql_quick_preview_supports_format(gl_quick_preview_state, mode.pixel_format));
// Pick an available buffer
MPProcessPipelineBuffer *output_buffer = NULL;
for (size_t i = 0; i < NUM_BUFFERS; ++i) {
@ -311,8 +273,6 @@ process_image_for_preview(const uint8_t *image)
#ifdef RENDERDOC
if (rdoc_api) rdoc_api->StartFrameCapture(NULL, NULL);
#endif
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
check_gl();
// Copy image to a GL texture. TODO: This can be avoided
GLuint input_texture;
@ -325,24 +285,13 @@ process_image_for_preview(const uint8_t *image)
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, mode.width, mode.height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image);
check_gl();
gl_quick_preview(
gl_quick_preview_state,
output_buffer->texture_id, output_buffer_width, output_buffer_height,
input_texture, mode.width, mode.height,
mode.pixel_format,
camera->rotate, camera->mirrored,
camera->previewmatrix[0] == 0 ? NULL : camera->previewmatrix,
camera->blacklevel);
gles2_debayer_process(
gles2_debayer, output_buffer->texture_id, input_texture);
check_gl();
// surface = cairo_image_surface_create(
// CAIRO_FORMAT_RGB24, preview_width, preview_height);
// uint32_t *pixels = (uint32_t *)cairo_image_surface_get_data(surface);
glFinish();
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteTextures(1, &input_texture);
clock_t t2 = clock();
printf("%fms\n", (float)(t2 - t1) / CLOCKS_PER_SEC * 1000);
@ -727,10 +676,10 @@ mp_process_pipeline_capture()
}
static void
update_output_buffers()
on_output_changed()
{
output_buffer_width = mode.width / 2;
output_buffer_height = mode.height / 2;
int output_buffer_width = mode.width / 2;
int output_buffer_height = mode.height / 2;
if (camera->rotate != 0 || camera->rotate != 180) {
int tmp = output_buffer_width;
@ -744,12 +693,20 @@ update_output_buffers()
}
glBindTexture(GL_TEXTURE_2D, 0);
gles2_debayer_configure(
gles2_debayer,
output_buffer_width, output_buffer_height,
mode.width, mode.height,
camera->rotate, camera->mirrored,
camera->previewmatrix[0] == 0 ? NULL : camera->previewmatrix,
camera->blacklevel);
}
static void
update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state)
{
const bool buffer_update_required = (!mp_camera_mode_is_equivalent(&mode, &state->mode) || preview_width != state->preview_width || preview_height != state->preview_height);
const bool output_changed = (!mp_camera_mode_is_equivalent(&mode, &state->mode) || preview_width != state->preview_width || preview_height != state->preview_height);
camera = state->camera;
mode = state->mode;
@ -766,8 +723,8 @@ update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state
exposure_is_manual = state->exposure_is_manual;
exposure = state->exposure;
if (buffer_update_required) {
update_output_buffers();
if (output_changed) {
on_output_changed();
}
struct mp_main_state main_state = {

View File

@ -1,120 +0,0 @@
#include "quickpreview.h"
#include <assert.h>
#include <glib.h>
#include <stdio.h>
static void
test_quick_preview_fuzz()
{
for (size_t i = 0; i < 10000; ++i) {
uint32_t width = rand() % 127 + 1;
uint32_t height = rand() % 127 + 1;
uint32_t format = rand() % (MP_PIXEL_FMT_MAX - 1) + 1;
assert(width > 0 && height > 0);
switch (format) {
case MP_PIXEL_FMT_BGGR8:
case MP_PIXEL_FMT_GBRG8:
case MP_PIXEL_FMT_GRBG8:
case MP_PIXEL_FMT_RGGB8:
width += width % 2;
height += height % 2;
break;
case MP_PIXEL_FMT_BGGR10P:
case MP_PIXEL_FMT_GBRG10P:
case MP_PIXEL_FMT_GRBG10P:
case MP_PIXEL_FMT_RGGB10P:
width += width % 2;
height += height % 2;
break;
case MP_PIXEL_FMT_UYVY:
case MP_PIXEL_FMT_YUYV:
width += width % 4;
break;
default:
assert(false);
}
int rotation;
switch (rand() % 3) {
case 0:
rotation = 0;
break;
case 1:
rotation = 90;
break;
case 2:
rotation = 180;
break;
default:
rotation = 270;
break;
}
bool mirrored = rand() % 2;
float matbuf[9];
float *colormatrix = NULL;
if (rand() % 2) {
for (int j = 0; j < 9; ++j) {
matbuf[j] = (double)rand() / RAND_MAX * 2.0;
}
colormatrix = matbuf;
}
uint8_t blacklevel = rand() % 3;
size_t src_size = mp_pixel_format_width_to_bytes(format, width) * height;
uint8_t *src = malloc(src_size);
for (int j = 0; j < src_size; ++j) {
src[j] = rand();
}
uint32_t preview_width = mp_pixel_format_width_to_colors(format, width);
uint32_t preview_height = mp_pixel_format_height_to_colors(format, height);
if (preview_width > 32 && preview_height > 32) {
preview_width /= (1 + rand() % 2);
preview_height /= (1 + rand() % 2);
}
uint32_t dst_width, dst_height, skip;
quick_preview_size(
&dst_width,
&dst_height,
&skip,
preview_width,
preview_height,
width,
height,
format,
rotation);
uint32_t *dst = malloc(dst_width * dst_height * sizeof(uint32_t));
quick_preview(
dst,
dst_width,
dst_height,
src,
width,
height,
format,
rotation,
mirrored,
colormatrix,
blacklevel,
skip);
free(dst);
free(src);
}
}
int
main(int argc, char *argv[])
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/quick_preview/fuzz", test_quick_preview_fuzz);
return g_test_run();
}