diff --git a/camera.glade b/camera.glade
deleted file mode 100644
index 89d7872..0000000
--- a/camera.glade
+++ /dev/null
@@ -1,592 +0,0 @@
-
-
-
-
-
-
-
diff --git a/data/blit.frag b/data/blit.frag
index 43ea399..1f1b314 100644
--- a/data/blit.frag
+++ b/data/blit.frag
@@ -1,4 +1,6 @@
+#ifdef GL_ES
precision mediump float;
+#endif
uniform sampler2D texture;
diff --git a/data/blit.vert b/data/blit.vert
index b4576b8..557c872 100644
--- a/data/blit.vert
+++ b/data/blit.vert
@@ -1,4 +1,6 @@
+#ifdef GL_ES
precision mediump float;
+#endif
attribute vec2 vert;
attribute vec2 tex_coord;
diff --git a/camera.css b/data/camera.css
similarity index 100%
rename from camera.css
rename to data/camera.css
diff --git a/camera.ui b/data/camera.ui
similarity index 99%
rename from camera.ui
rename to data/camera.ui
index bb06696..9b137f0 100644
--- a/camera.ui
+++ b/data/camera.ui
@@ -25,6 +25,7 @@
1
0
+ 1
diff --git a/data/debayer.frag b/data/debayer.frag
index 94ecc50..8e9f184 100644
--- a/data/debayer.frag
+++ b/data/debayer.frag
@@ -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;
diff --git a/data/debayer.vert b/data/debayer.vert
index d06d511..49af8c1 100644
--- a/data/debayer.vert
+++ b/data/debayer.vert
@@ -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);
}
diff --git a/folder-symbolic.svg b/data/folder-symbolic.svg
similarity index 100%
rename from folder-symbolic.svg
rename to data/folder-symbolic.svg
diff --git a/data/meson.build b/data/meson.build
new file mode 100644
index 0000000..c0de055
--- /dev/null
+++ b/data/meson.build
@@ -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')
diff --git a/data/org.postmarketos.Megapixels.gresource.xml b/data/org.postmarketos.Megapixels.gresource.xml
new file mode 100644
index 0000000..c71c8db
--- /dev/null
+++ b/data/org.postmarketos.Megapixels.gresource.xml
@@ -0,0 +1,15 @@
+
+
+
+ camera.ui
+ camera.css
+ switch-camera.svg
+ shutter-button.svg
+ folder-symbolic.svg
+ settings-symbolic.svg
+ blit.vert
+ blit.frag
+ debayer.vert
+ debayer.frag
+
+
diff --git a/postprocess.sh b/data/postprocess.sh
similarity index 100%
rename from postprocess.sh
rename to data/postprocess.sh
diff --git a/settings-symbolic.svg b/data/settings-symbolic.svg
similarity index 100%
rename from settings-symbolic.svg
rename to data/settings-symbolic.svg
diff --git a/shutter-button.svg b/data/shutter-button.svg
similarity index 100%
rename from shutter-button.svg
rename to data/shutter-button.svg
diff --git a/switch-camera.svg b/data/switch-camera.svg
similarity index 100%
rename from switch-camera.svg
rename to data/switch-camera.svg
diff --git a/gl_quickpreview.c b/gl_quickpreview.c
deleted file mode 100644
index 38df81e..0000000
--- a/gl_quickpreview.c
+++ /dev/null
@@ -1,207 +0,0 @@
-#include "camera.h"
-#include "gl_quickpreview.h"
-#include "gl_utils.h"
-#include
-
-
-#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;
-}
diff --git a/gl_quickpreview.h b/gl_quickpreview.h
deleted file mode 100644
index fede8c5..0000000
--- a/gl_quickpreview.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "quickpreview.h"
-#include
-#include
-#include
-
-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);
diff --git a/gl_utils.c b/gl_utils.c
deleted file mode 100644
index f32bf98..0000000
--- a/gl_utils.c
+++ /dev/null
@@ -1,128 +0,0 @@
-#include "gl_utils.h"
-
-#include
-#include
-#include
-#include
-
-void __check_gl(const char *file, int line)
-{
- GLenum error = glGetError();
-
- const char *name;
- switch (error) {
- case GL_NO_ERROR:
- return; // no error
- case GL_INVALID_ENUM:
- name = "GL_INVALID_ENUM";
- break;
- case GL_INVALID_VALUE:
- name = "GL_INVALID_VALUE";
- break;
- case GL_INVALID_OPERATION:
- name = "GL_INVALID_OPERATION";
- break;
- case GL_INVALID_FRAMEBUFFER_OPERATION:
- name = "GL_INVALID_FRAMEBUFFER_OPERATION";
- break;
- case GL_OUT_OF_MEMORY:
- name = "GL_OUT_OF_MEMORY";
- break;
- default:
- name = "UNKNOWN ERROR!";
- break;
- }
-
- printf("GL error at %s:%d - %s\n", file, line, name);
-}
-
-const GLfloat gl_quad_vertices[] = {
- -1, -1,
- 1, -1,
- -1, 1,
- 1, 1,
-};
-
-const GLfloat gl_quad_texcoords[] = {
- 0, 0,
- 1, 0,
- 0, 1,
- 1, 1,
-};
-
-GLuint
-gl_load_shader(const char *path, GLenum type)
-{
- check_gl();
-
- FILE *f = fopen(path, "r");
-
- fseek(f, 0, SEEK_END);
- GLint size = ftell(f);
- char *source = malloc(sizeof(char) * size);
-
- fseek(f, 0, SEEK_SET);
- if (fread(source, size, 1, f) == 0) {
- printf("Failed to read shader file\n");
- return 0;
- }
-
- GLuint shader = glCreateShader(type);
- assert(shader != 0);
- glShaderSource(shader, 1, (const GLchar * const*)&source, &size);
- glCompileShader(shader);
- check_gl();
-
- // Check compile status
- GLint success;
- glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
- if (success == GL_FALSE) {
- printf("Shader compilation failed for %s\n", path);
- }
-
- GLint log_length;
- glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
- if (log_length > 0) {
- char *log = malloc(sizeof(char) * log_length);
- glGetShaderInfoLog(shader, log_length - 1, &log_length, log);
-
- printf("Shader %s log: %s\n", path, log);
- free(log);
- }
-
- free(source);
-
- return shader;
-}
-
-GLuint
-gl_link_program(GLuint *shaders, size_t num_shaders)
-{
- GLuint program = glCreateProgram();
-
- for (size_t i = 0; i < num_shaders; ++i) {
- glAttachShader(program, shaders[i]);
- }
-
- glLinkProgram(program);
- check_gl();
-
- GLint success;
- glGetProgramiv(program, GL_LINK_STATUS, &success);
- if (success == GL_FALSE) {
- printf("Program linking failed\n");
- }
-
- GLint log_length;
- glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
- if (log_length > 0) {
- char *log = malloc(sizeof(char) * log_length);
- glGetProgramInfoLog(program, log_length - 1, &log_length, log);
-
- printf("Program log: %s\n", log);
- free(log);
- }
- check_gl();
-
- return program;
-}
diff --git a/gl_utils.h b/gl_utils.h
deleted file mode 100644
index 097c86a..0000000
--- a/gl_utils.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-#include
-#include
-
-#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);
diff --git a/meson.build b/meson.build
index ef331a6..163fc20 100644
--- a/meson.build
+++ b/meson.build
@@ -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)
diff --git a/org.postmarketos.Megapixels.gresource.xml b/org.postmarketos.Megapixels.gresource.xml
deleted file mode 100644
index d9938c1..0000000
--- a/org.postmarketos.Megapixels.gresource.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
- camera.glade
- camera.css
- switch-camera.svg
- shutter-button.svg
- folder-symbolic.svg
- settings-symbolic.svg
-
-
diff --git a/quickpreview.c b/quickpreview.c
deleted file mode 100644
index 541b62e..0000000
--- a/quickpreview.c
+++ /dev/null
@@ -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
-#include
-
-/* 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;
-}
diff --git a/quickpreview.h b/quickpreview.h
deleted file mode 100644
index 1815215..0000000
--- a/quickpreview.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#include "camera.h"
-#include
-
-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);
diff --git a/camera.c b/src/camera.c
similarity index 100%
rename from camera.c
rename to src/camera.c
diff --git a/camera.h b/src/camera.h
similarity index 100%
rename from camera.h
rename to src/camera.h
diff --git a/camera_config.c b/src/camera_config.c
similarity index 100%
rename from camera_config.c
rename to src/camera_config.c
diff --git a/camera_config.h b/src/camera_config.h
similarity index 100%
rename from camera_config.h
rename to src/camera_config.h
diff --git a/device.c b/src/device.c
similarity index 100%
rename from device.c
rename to src/device.c
diff --git a/device.h b/src/device.h
similarity index 100%
rename from device.h
rename to src/device.h
diff --git a/src/gl_util.c b/src/gl_util.c
new file mode 100644
index 0000000..c4c76ca
--- /dev/null
+++ b/src/gl_util.c
@@ -0,0 +1,220 @@
+#include "gl_util.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+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();
+}
diff --git a/src/gl_util.h b/src/gl_util.h
new file mode 100644
index 0000000..f1e8451
--- /dev/null
+++ b/src/gl_util.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include
+#include
+
+#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);
diff --git a/src/gles2_debayer.c b/src/gles2_debayer.c
new file mode 100644
index 0000000..df31fc4
--- /dev/null
+++ b/src/gles2_debayer.c
@@ -0,0 +1,141 @@
+#include "gles2_debayer.h"
+
+#include "camera.h"
+#include "gl_util.h"
+#include
+
+#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);
+}
diff --git a/src/gles2_debayer.h b/src/gles2_debayer.h
new file mode 100644
index 0000000..0028749
--- /dev/null
+++ b/src/gles2_debayer.h
@@ -0,0 +1,21 @@
+#include "camera.h"
+#include "gl_util.h"
+#include
+#include
+
+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);
diff --git a/ini.c b/src/ini.c
similarity index 100%
rename from ini.c
rename to src/ini.c
diff --git a/ini.h b/src/ini.h
similarity index 100%
rename from ini.h
rename to src/ini.h
diff --git a/io_pipeline.c b/src/io_pipeline.c
similarity index 100%
rename from io_pipeline.c
rename to src/io_pipeline.c
diff --git a/io_pipeline.h b/src/io_pipeline.h
similarity index 100%
rename from io_pipeline.h
rename to src/io_pipeline.h
diff --git a/main.c b/src/main.c
similarity index 95%
rename from main.c
rename to src/main.c
index 3e38311..fb3af54 100644
--- a/main.c
+++ b/src/main.c
@@ -18,9 +18,8 @@
#include
#include
#include
-#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);
}
diff --git a/main.h b/src/main.h
similarity index 100%
rename from main.h
rename to src/main.h
diff --git a/matrix.c b/src/matrix.c
similarity index 100%
rename from matrix.c
rename to src/matrix.c
diff --git a/matrix.h b/src/matrix.h
similarity index 100%
rename from matrix.h
rename to src/matrix.h
diff --git a/pipeline.c b/src/pipeline.c
similarity index 100%
rename from pipeline.c
rename to src/pipeline.c
diff --git a/pipeline.h b/src/pipeline.h
similarity index 100%
rename from pipeline.h
rename to src/pipeline.h
diff --git a/process_pipeline.c b/src/process_pipeline.c
similarity index 85%
rename from process_pipeline.c
rename to src/process_pipeline.c
index 8e178ff..e6dc53a 100644
--- a/process_pipeline.c
+++ b/src/process_pipeline.c
@@ -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
#include
#include
#include
#include
-#include "gl_utils.h"
-#include
-#include
-// #include
+#include "gl_util.h"
#include
#include
@@ -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 = {
diff --git a/process_pipeline.h b/src/process_pipeline.h
similarity index 100%
rename from process_pipeline.h
rename to src/process_pipeline.h
diff --git a/renderdoc/app.h b/src/renderdoc/app.h
similarity index 100%
rename from renderdoc/app.h
rename to src/renderdoc/app.h
diff --git a/zbar_pipeline.c b/src/zbar_pipeline.c
similarity index 100%
rename from zbar_pipeline.c
rename to src/zbar_pipeline.c
diff --git a/zbar_pipeline.h b/src/zbar_pipeline.h
similarity index 100%
rename from zbar_pipeline.h
rename to src/zbar_pipeline.h
diff --git a/tests/test_quickpreview.c b/tests/test_quickpreview.c
deleted file mode 100644
index 9b43778..0000000
--- a/tests/test_quickpreview.c
+++ /dev/null
@@ -1,120 +0,0 @@
-#include "quickpreview.h"
-#include
-#include
-#include
-
-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();
-}