1 #include "chroma_subsampler.h"
4 #include <movit/util.h>
6 #define BUFFER_OFFSET(i) ((char *)nullptr + (i))
10 string read_file(const string &filename);
11 GLuint compile_shader(const string &shader_src, GLenum type);
12 GLuint link_program(GLuint vs_obj, GLuint fs_obj);
13 void bind_sampler(GLuint program, GLint location, GLuint texture_unit, GLuint tex, GLuint sampler);
15 extern GLuint linear_sampler;
17 ChromaSubsampler::ChromaSubsampler()
19 // Set up stuff for 4:2:2 conversion.
21 // Note: Due to the horizontally co-sited chroma/luma samples in H.264
22 // (chroma position is left for horizontal),
23 // we need to be a bit careful in our subsampling. A diagram will make
24 // this clearer, showing some luma and chroma samples:
39 // Clearly, the rightmost chroma sample here needs to be equivalent to
40 // b/4 + c/2 + d/4. (We could also implement more sophisticated filters,
41 // of course, but as long as the upsampling is not going to be equally
42 // sophisticated, it's probably not worth it.) If we sample once with
43 // no mipmapping, we get just c, ie., no actual filtering in the
44 // horizontal direction. (For the vertical direction, we can just
45 // sample in the middle to get the right filtering.) One could imagine
46 // we could use mipmapping (assuming we can create mipmaps cheaply),
47 // but then, what we'd get is this:
62 // which ends up sampling equally from a and b, which clearly isn't right. Instead,
63 // we need to do two (non-mipmapped) chroma samples, both hitting exactly in-between
66 // Sampling in-between b and c gives us the sample (b+c)/2, and similarly for c and d.
67 // Taking the average of these gives of (b+c)/4 + (c+d)/4 = b/4 + c/2 + d/4, which is
68 // exactly what we want.
70 // See also http://www.poynton.com/PDFs/Merging_RGB_and_422.pdf, pages 6–7.
72 cbcr_vs_obj = compile_shader(read_file("chroma_subsample.vert"), GL_VERTEX_SHADER);
73 cbcr_fs_obj = compile_shader(read_file("chroma_subsample.frag"), GL_FRAGMENT_SHADER);
74 cbcr_program = link_program(cbcr_vs_obj, cbcr_fs_obj);
76 // Set up the VAO containing all the required position data.
77 glCreateVertexArrays(1, &vao);
78 glBindVertexArray(vao);
85 glCreateBuffers(1, &vbo);
86 glNamedBufferData(vbo, sizeof(vertices), vertices, GL_STATIC_DRAW);
87 glBindBuffer(GL_ARRAY_BUFFER, vbo);
89 GLint position_attrib = 0; // Hard-coded in every vertex shader.
90 glEnableVertexArrayAttrib(vao, position_attrib);
91 glVertexAttribPointer(position_attrib, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
93 uniform_cbcr_tex = glGetUniformLocation(cbcr_program, "cbcr_tex");
94 uniform_chroma_offset_0 = glGetUniformLocation(cbcr_program, "chroma_offset_0");
95 uniform_chroma_offset_1 = glGetUniformLocation(cbcr_program, "chroma_offset_1");
98 ChromaSubsampler::~ChromaSubsampler()
100 glDeleteProgram(cbcr_program);
102 glDeleteBuffers(1, &vbo);
104 glDeleteVertexArrays(1, &vao);
108 void ChromaSubsampler::subsample_chroma(GLuint cbcr_tex, unsigned width, unsigned height, GLuint cb_tex, GLuint cr_tex)
110 glUseProgram(cbcr_program);
111 bind_sampler(cbcr_program, uniform_cbcr_tex, 0, cbcr_tex, linear_sampler);
112 glProgramUniform2f(cbcr_program, uniform_chroma_offset_0, -1.0f / width, 0.0f);
113 glProgramUniform2f(cbcr_program, uniform_chroma_offset_1, -0.0f / width, 0.0f);
115 glViewport(0, 0, width/2, height);
116 fbos.render_to(cb_tex, cr_tex);
118 glBindVertexArray(vao);
119 glDrawArrays(GL_TRIANGLES, 0, 3);