Browse Source

Reorganization, replacing CPU debayering entirely

opengl
Benjamin Schaaf 2 months ago
parent
commit
91817b167a
  1. 592
      camera.glade
  2. 2
      data/blit.frag
  3. 2
      data/blit.vert
  4. 0
      data/camera.css
  5. 1
      data/camera.ui
  6. 39
      data/debayer.frag
  7. 15
      data/debayer.vert
  8. 0
      data/folder-symbolic.svg
  9. 15
      data/meson.build
  10. 15
      data/org.postmarketos.Megapixels.gresource.xml
  11. 0
      data/postprocess.sh
  12. 0
      data/settings-symbolic.svg
  13. 0
      data/shutter-button.svg
  14. 0
      data/switch-camera.svg
  15. 207
      gl_quickpreview.c
  16. 21
      gl_quickpreview.h
  17. 128
      gl_utils.c
  18. 13
      gl_utils.h
  19. 90
      meson.build
  20. 11
      org.postmarketos.Megapixels.gresource.xml
  21. 351
      quickpreview.c
  22. 14
      quickpreview.h
  23. 0
      src/camera.c
  24. 0
      src/camera.h
  25. 0
      src/camera_config.c
  26. 0
      src/camera_config.h
  27. 0
      src/device.c
  28. 0
      src/device.h
  29. 220
      src/gl_util.c
  30. 17
      src/gl_util.h
  31. 141
      src/gles2_debayer.c
  32. 21
      src/gles2_debayer.h
  33. 0
      src/ini.c
  34. 0
      src/ini.h
  35. 0
      src/io_pipeline.c
  36. 0
      src/io_pipeline.h
  37. 70
      src/main.c
  38. 0
      src/main.h
  39. 0
      src/matrix.c
  40. 0
      src/matrix.h
  41. 0
      src/pipeline.c
  42. 0
      src/pipeline.h
  43. 123
      src/process_pipeline.c
  44. 0
      src/process_pipeline.h
  45. 0
      src/renderdoc/app.h
  46. 0
      src/zbar_pipeline.c
  47. 0
      src/zbar_pipeline.h
  48. 120
      tests/test_quickpreview.c

592
camera.glade

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

2
data/blit.frag

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

2
data/blit.vert

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

0
camera.css → data/camera.css

1
camera.ui → data/camera.ui

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

39
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));
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;
// 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);
// 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;

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

0
folder-symbolic.svg → data/folder-symbolic.svg

15
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')

15
data/org.postmarketos.Megapixels.gresource.xml

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

0
postprocess.sh → data/postprocess.sh

0
settings-symbolic.svg → data/settings-symbolic.svg

0
shutter-button.svg → data/shutter-button.svg

0
switch-camera.svg → data/switch-camera.svg

207
gl_quickpreview.c

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

21
gl_quickpreview.h

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

128
gl_utils.c

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

13
gl_utils.h

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

90
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')
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')
'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.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)

11
org.postmarketos.Megapixels.gresource.xml

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

351
quickpreview.c

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

14
quickpreview.h

@ -1,14 +0,0 @@
#include "camera.h"