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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/* SPDX-License-Identifier: LGPL-2.1-or-later

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

pub mod bayer;
pub mod crop;
pub mod passthrough;
pub mod yuv;

use glium::framebuffer::ValidationError;
use glium::texture::UnsignedTexture2d;
use thiserror::Error;
use crate::egl::Dimensions;
use crate::egl::Texture;
use crate::import_bind_texture;
use crate::uniform_extend;
use crate::uniforms::ExtendUniforms;
use glium::{self, IndexBuffer, VertexBuffer, Program, Surface};
use glium::uniforms::Uniforms;
use std::error;
use std::marker::PhantomData;


#[derive(Error, Debug)]
enum Error {
    #[error("Transforming source texture doesn't create an image matching target dimensions")]
    BadDimensions(&'static str),
    #[error("Source is not in a supported format")]
    BadFormat(&'static str),
}


#[derive(Copy, Clone, Debug)]
struct Vertex {
    position: [f32; 2],
    tex_coords: [f32; 2],
}

glium::implement_vertex!(Vertex, position, tex_coords);

/// A stretched triangle to cover all area. No need for a quad.
/// Half of the triangle will be outside of the surface.
///
/// ◣  
/// □◣
fn covering_vertices<F: glium::backend::Facade>(facade: &F, (width, height): (u32, u32))
    -> Result<(VertexBuffer<Vertex>, IndexBuffer<u16>), Box<dyn error::Error>>
{
    Ok((
        VertexBuffer::new(
            facade,
            &[
                // left bottom
                Vertex { position: [-1.0, -3.0], tex_coords: [0.0, height as f32 * 2.] },
                // left top
                Vertex { position: [-1.0,  1.0], tex_coords: [0.0, 0.0] },
                // right top
                Vertex { position: [3.0,  1.0], tex_coords: [width as f32 * 2., 0.0] },
            ]
        )?,
        IndexBuffer::new(
            facade,
            glium::index::PrimitiveType::TriangleStrip,
            &[1 as u16, 2, 0],
        )?,
     ))
}


/// This struct stores static properties of the shader between executions.
/// This includes the output resolution.
pub struct Shader<T>{
    // We can get away with keeping one type for all shaders of this type,
    // rather than defining a trait, because the Program type is always the same.
    program: Program,
    // Buffer objects live on the GPU, so creating them on each draw call would introduce unnecessary delays.
    vertices: glium::VertexBuffer<Vertex>,
    indices: glium::IndexBuffer<u16>,
    _data: PhantomData<T>,
}

// FIXME: make sure UnsignedTexture2d does not outlive the image
pub fn import_target_texture(
    facade: &impl glium::backend::Facade,
    target_tex: &Texture,
) -> UnsignedTexture2d {
    let Dimensions { width, height } = target_tex.dimensions();
    unsafe {
        use glium::texture::*;
        UnsignedTexture2d::from_id(
            facade,
            UncompressedUintFormat::U8U8U8U8,
            target_tex.id().as_gl_id(),
            false,
            MipmapsOption::NoMipmap,
            Dimensions::Texture2d { width, height },
        )
    }
}

/// Makes it possibleto write to this texture
pub fn texture_to_surface<'a>(facade: &impl glium::backend::Facade, target: &'a mut UnsignedTexture2d)
    -> Result<glium::framebuffer::SimpleFrameBuffer<'a>, ValidationError>
{
        // We have no surface in the current context yet,
        // so DefaultFrameBuffer is not useful here, as it renders to the pre-existing surface.
        // Instead, using a frame buffer with an explicit attachment.
        glium::framebuffer::SimpleFrameBuffer::new(facade, &*target)
}

impl<W> Shader<W> {    
    /// Draws the shader with generic parameters
    fn draw_any<'a, F: glium::backend::Facade>(
        &self,
        facade: &F,
        source_tex: Texture,
        target: &mut impl Surface,
        uniforms: impl Uniforms + ExtendUniforms<'a>,
    ) -> Result<(), Box<dyn error::Error>>{
        let Dimensions { width: source_width, height: source_height } = source_tex.dimensions();
        let source = import_bind_texture(facade, source_tex)?;

        let uniforms = uniform_extend! {
            uniforms,
            // FIXME: needed for debugging.
            // Should the drawing procedure be generalized into an interface that can take different kinds of data?
            frameno: 0,
            source_dims: (
                source_width as f32, source_height as f32,
                1.0 / source_width as f32, 1.0 / source_height as f32,
            ),
            source: source
                .sampled()
                .wrap_function(glium::uniforms::SamplerWrapFunction::Repeat)
                .minify_filter(glium::uniforms::MinifySamplerFilter::Nearest)
                .magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest),
        };
        
        target.clear_color(0.0, 0.0, 1.0, 1.0);
        target.draw(
            &self.vertices,
            &self.indices,
            &self.program,
            &uniforms,
            &Default::default(),
        )?;

        // GlFlush should instruct GBM (or whatever reader) to block when reading the buffer.
        facade.get_context().flush();
        // FIXME: But it doesn't. Moving on to more important problems.
        facade.get_context().finish();
        Ok(())
    }
}