Add an effect for 4:2:2 interleaved YCbCr input (UYVY).
[movit] / ycbcr_422interleaved_input.cpp
1 #include <epoxy/gl.h>
2 #include <assert.h>
3 #include <stdio.h>
4 #include <string.h>
5
6 #include "effect_util.h"
7 #include "resource_pool.h"
8 #include "util.h"
9 #include "ycbcr.h"
10 #include "ycbcr_422interleaved_input.h"
11
12 using namespace Eigen;
13 using namespace std;
14
15 namespace movit {
16
17 YCbCr422InterleavedInput::YCbCr422InterleavedInput(const ImageFormat &image_format,
18                                                    const YCbCrFormat &ycbcr_format,
19                                                    unsigned width, unsigned height)
20         : image_format(image_format),
21           ycbcr_format(ycbcr_format),
22           width(width),
23           height(height),
24           resource_pool(NULL)
25 {
26         pbo = 0;
27         texture_num[0] = texture_num[1] = 0;
28
29         assert(ycbcr_format.chroma_subsampling_x == 2);
30         assert(ycbcr_format.chroma_subsampling_y == 1);
31         assert(width % ycbcr_format.chroma_subsampling_x == 0);
32
33         widths[CHANNEL_LUMA] = width;
34         widths[CHANNEL_CHROMA] = width / ycbcr_format.chroma_subsampling_x;
35         pitches[CHANNEL_LUMA] = width;
36         pitches[CHANNEL_CHROMA] = width / ycbcr_format.chroma_subsampling_x;
37
38         pixel_data = NULL;
39 }
40
41 YCbCr422InterleavedInput::~YCbCr422InterleavedInput()
42 {
43         for (unsigned channel = 0; channel < 2; ++channel) {
44                 if (texture_num[channel] != 0) {
45                         resource_pool->release_2d_texture(texture_num[channel]);
46                 }
47         }
48 }
49
50 void YCbCr422InterleavedInput::set_gl_state(GLuint glsl_program_num, const string& prefix, unsigned *sampler_num)
51 {
52         for (unsigned channel = 0; channel < 2; ++channel) {
53                 glActiveTexture(GL_TEXTURE0 + *sampler_num + channel);
54                 check_error();
55
56                 if (texture_num[channel] == 0) {
57                         // (Re-)upload the texture.
58                         GLuint format, internal_format;
59                         if (channel == CHANNEL_LUMA) {
60                                 format = GL_RG;
61                                 internal_format = GL_RG8;
62                         } else {        
63                                 assert(channel == CHANNEL_CHROMA);
64                                 format = GL_RGBA;
65                                 internal_format = GL_RGBA8;
66                         }
67
68                         texture_num[channel] = resource_pool->create_2d_texture(internal_format, widths[channel], height);
69                         glBindTexture(GL_TEXTURE_2D, texture_num[channel]);
70                         check_error();
71                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
72                         check_error();
73                         glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbo);
74                         check_error();
75                         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
76                         check_error();
77                         glPixelStorei(GL_UNPACK_ROW_LENGTH, pitches[channel]);
78                         check_error();
79                         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, widths[channel], height, format, GL_UNSIGNED_BYTE, pixel_data);
80                         check_error();
81                         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
82                         check_error();
83                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
84                         check_error();
85                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
86                         check_error();
87                 } else {
88                         glBindTexture(GL_TEXTURE_2D, texture_num[channel]);
89                         check_error();
90                 }
91         }
92
93         glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
94         check_error();
95
96         // Bind samplers.
97         set_uniform_int(glsl_program_num, prefix, "tex_y", *sampler_num + 0);
98         set_uniform_int(glsl_program_num, prefix, "tex_cbcr", *sampler_num + 1);
99
100         *sampler_num += 2;
101 }
102
103 string YCbCr422InterleavedInput::output_fragment_shader()
104 {
105         float offset[3];
106         Matrix3d ycbcr_to_rgb;
107         compute_ycbcr_matrix(ycbcr_format, offset, &ycbcr_to_rgb);
108
109         string frag_shader;
110
111         frag_shader = output_glsl_mat3("PREFIX(inv_ycbcr_matrix)", ycbcr_to_rgb);
112         frag_shader += output_glsl_vec3("PREFIX(offset)", offset[0], offset[1], offset[2]);
113
114         float cb_offset_x = compute_chroma_offset(
115                 ycbcr_format.cb_x_position, ycbcr_format.chroma_subsampling_x, widths[CHANNEL_CHROMA]);
116         float cr_offset_x = compute_chroma_offset(
117                 ycbcr_format.cr_x_position, ycbcr_format.chroma_subsampling_x, widths[CHANNEL_CHROMA]);
118         frag_shader += output_glsl_float("PREFIX(cb_offset_x)", cb_offset_x);
119         frag_shader += output_glsl_float("PREFIX(cr_offset_x)", cr_offset_x);
120
121         char buf[256];
122         sprintf(buf, "#define CB_CR_OFFSETS_EQUAL %d\n",
123                 (fabs(ycbcr_format.cb_x_position - ycbcr_format.cr_x_position) < 1e-6));
124         frag_shader += buf;
125
126         frag_shader += read_file("ycbcr_422interleaved_input.frag");
127         return frag_shader;
128 }
129
130 void YCbCr422InterleavedInput::invalidate_pixel_data()
131 {
132         for (unsigned channel = 0; channel < 2; ++channel) {
133                 if (texture_num[channel] != 0) {
134                         resource_pool->release_2d_texture(texture_num[channel]);
135                         texture_num[channel] = 0;
136                 }
137         }
138 }
139
140 bool YCbCr422InterleavedInput::set_int(const std::string& key, int value)
141 {
142         if (key == "needs_mipmaps") {
143                 // We currently do not support this.
144                 return (value == 0);
145         }
146         return Effect::set_int(key, value);
147 }
148
149 }  // namespace movit