1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/* SPDX-License-Identifier: LGPL-2.1-or-later

Copyright (c) 2022 Purism, SPC
Copyright (c) 2024 DorotaC
*/

use cgmath::Matrix3;
use crate::egl::{ContextRef, CurrentContext, Texture};
use crate::raw;
use glium::{program, uniform};
use std::error::Error;
use super::Shader;

pub enum PixOrder {
    RGGB = 0,
    GRBG = 1,
    GBRG = 2,
    BGGR = 3,
}

impl TryFrom<u32> for PixOrder {
    type Error = ();
    fn try_from(v: u32) -> Result<Self, Self::Error> {
        use PixOrder::*;
        match v {
            0 => Ok(RGGB),
            1 => Ok(GRBG),
            2 => Ok(GBRG),
            3 => Ok(BGGR),
            _ => Err(()),
        }
    }
}

/// Converts a Bayer pattern image into RGBA, preserving the resolution.
/// Uses the high quality debayering algorithm.
pub struct DebayerHq;

impl DebayerHq {
    /// Creates the shader
    pub fn new_raw<F: glium::backend::Facade>(facade: &F, out_dims: (u32, u32))
        -> Result<Shader<Self>, Box<dyn Error>>
    {
        Shader::new_with_facade(facade, out_dims)
    }

    /// Creates the shader
    /// Each subpixel of the data in the input is encoded across two bytes of the texture.
    pub fn new_raw_rg88<F: glium::backend::Facade>(facade: &F, out_dims: (u32, u32))
        -> Result<Shader<Self>, Box<dyn Error>>
    {
        Shader::new_rg88_with_facade(facade, out_dims)
    }
}


impl Shader<DebayerHq> {
    pub fn new_with_facade<F: glium::backend::Facade>(
        facade: &F,
        out_dims: (u32, u32),
    ) -> Result<Self, Box<dyn Error>> {
        Self::new_with_facade_and_fragment(facade, out_dims, include_str!("bayer_hq/frag.glsl"))
    }

    pub fn new_rg88_with_facade<F: glium::backend::Facade>(
        facade: &F,
        out_dims: (u32, u32),
    ) -> Result<Self, Box<dyn Error>> {
        Self::new_with_facade_and_fragment(facade, out_dims, include_str!("bayer_hq/frag_rg.glsl"))
    }
    
    // private API: other modules have no business selecting the fragment shader.
    /// The only thing that differs between the native R16 and RG88 versions is the fragment shader.
    fn new_with_facade_and_fragment<F: glium::backend::Facade>(
        facade: &F,
        // Output dimensions
        out_dims: (u32, u32),
        fragment: &'static str,
    ) -> Result<Self, Box<dyn Error>> {
        let (vertices, indices) = super::covering_vertices(facade, out_dims)?;
        Ok(Self {
            program: program!(facade,
                120 => {
                    vertex: include_str!("bayer_hq/vert.glsl"),
                    fragment: fragment,
                }
            )?,
            vertices,
            indices,
            _data: Default::default(),
        })
    }
    
    /// Debayers GL texture in R8 format `source_tex`
    /// to GL texture in RGBA `target_tex`.
    /// Both must be of the `width`/`height` pixel dimensions.
    /// PixOrder defines the subpixel order in `source_tex`.
    pub fn convert<F: glium::backend::Facade>(
        &self,
        facade: &F,
        source_tex: Texture,
        target: &mut impl glium::Surface,
        data: PixOrder,
        color_matrix: Matrix3<f32>,
    ) -> Result<(), Box<dyn Error>>{
        let color_matrix: [[f32; 3]; 3] = color_matrix.into();
        self.draw_any(
            facade,
            source_tex,
            target,
            uniform! {
                first_red: match data {
                    PixOrder::RGGB => (0f32, 0f32),
                    PixOrder::GRBG => (0f32, 1f32),
                    PixOrder::GBRG => (1f32, 0f32),
                    PixOrder::BGGR => (1f32, 1f32),
                },
                color_matrix: color_matrix,
            },
        )
    }
}