1 #ifndef _MOVIT_RESAMPLE_EFFECT_H
2 #define _MOVIT_RESAMPLE_EFFECT_H 1
4 // High-quality image resizing, either up or down.
6 // The default scaling offered by the GPU (and as used in ResizeEffect)
7 // is bilinear (optionally mipmapped), which is not the highest-quality
8 // choice, especially for upscaling. ResampleEffect offers the three-lobed
9 // Lanczos kernel, which is among the most popular choices in image
10 // processing. While it does have its weaknesses, in particular a certain
11 // ringing/sharpening effect with artifacts that accumulate over several
12 // consecutive resizings, it is generally regarded as the best tradeoff.
14 // Works in two passes; first horizontal, then vertical (ResampleEffect,
15 // which is what the user is intended to use, instantiates two copies of
16 // SingleResamplePassEffect behind the scenes).
31 class SingleResamplePassEffect;
32 class ResampleComputeEffect;
34 // Public so that it can be benchmarked externally.
40 struct ScalingWeights {
41 unsigned src_bilinear_samples;
42 unsigned dst_samples, num_loops;
43 int int_radius; // FIXME: really here?
44 float scaling_factor; // FIXME: really here?
46 // Exactly one of these three is set.
47 std::unique_ptr<Tap<fp16_int_t>[]> bilinear_weights_fp16;
48 std::unique_ptr<Tap<float>[]> bilinear_weights_fp32;
49 std::unique_ptr<fp16_int_t[]> raw_weights;
51 enum class BilinearFormatConstraints {
55 ScalingWeights calculate_bilinear_scaling_weights(unsigned src_size, unsigned dst_size, float zoom, float offset, BilinearFormatConstraints constraints);
56 ScalingWeights calculate_raw_scaling_weights(unsigned src_size, unsigned dst_size, float zoom, float offset);
58 // A simple manager for support data stored in a 2D texture.
59 // Consider moving it to a shared location of more classes
60 // should need similar functionality.
61 class Support2DTexture {
66 void update(GLint width, GLint height, GLenum internal_format, GLenum format, GLenum type, const GLvoid * data);
67 GLint get_texnum() const { return texnum; }
71 GLint last_texture_width = -1, last_texture_height = -1;
72 GLenum last_texture_internal_format = GL_INVALID_ENUM;
75 class ResampleEffect : public Effect {
80 std::string effect_type_id() const override { return "ResampleEffect"; }
82 void inform_input_size(unsigned input_num, unsigned width, unsigned height) override;
84 std::string output_fragment_shader() override {
87 void set_gl_state(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num) override {
91 void rewrite_graph(EffectChain *graph, Node *self) override;
92 bool set_float(const std::string &key, float value) override;
96 void update_offset_and_zoom();
98 // If compute shaders are supported, contains the effect.
100 std::unique_ptr<ResampleComputeEffect> compute_effect_owner;
101 ResampleComputeEffect *compute_effect = nullptr;
103 // Both of these are owned by us if owns_effects is true (before finalize()),
104 // and otherwise owned by the EffectChain.
105 std::unique_ptr<SingleResamplePassEffect> hpass_owner, vpass_owner;
106 SingleResamplePassEffect *hpass = nullptr, *vpass = nullptr;
108 int input_width, input_height, output_width, output_height;
110 float offset_x, offset_y;
111 float zoom_x, zoom_y;
112 float zoom_center_x, zoom_center_y;
115 class SingleResamplePassEffect : public Effect {
117 // If parent is non-nullptr, calls to inform_input_size will be forwarded,
118 // so that it can inform both passes about the right input and output
120 SingleResamplePassEffect(ResampleEffect *parent);
121 ~SingleResamplePassEffect();
122 std::string effect_type_id() const override { return "SingleResamplePassEffect"; }
124 std::string output_fragment_shader() override;
126 bool needs_texture_bounce() const override { return true; }
127 bool needs_srgb_primaries() const override { return false; }
128 AlphaHandling alpha_handling() const override { return INPUT_PREMULTIPLIED_ALPHA_KEEP_BLANK; }
130 // We specifically do not want mipmaps on the input texture;
131 // they break minification.
132 MipmapRequirements needs_mipmaps() const override { return CANNOT_ACCEPT_MIPMAPS; }
134 void inform_added(EffectChain *chain) override { this->chain = chain; }
135 void inform_input_size(unsigned input_num, unsigned width, unsigned height) override {
136 if (parent != nullptr) {
137 parent->inform_input_size(input_num, width, height);
140 bool changes_output_size() const override { return true; }
141 bool sets_virtual_output_size() const override { return false; }
143 void get_output_size(unsigned *width, unsigned *height, unsigned *virtual_width, unsigned *virtual_height) const override {
144 *virtual_width = *width = this->output_width;
145 *virtual_height = *height = this->output_height;
148 void set_gl_state(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num) override;
150 enum Direction { HORIZONTAL = 0, VERTICAL = 1 };
153 void update_texture(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num);
155 ResampleEffect *parent;
158 GLint uniform_sample_tex;
159 float uniform_num_loops, uniform_slice_height, uniform_sample_x_scale, uniform_sample_x_offset;
160 float uniform_whole_pixel_offset;
161 int uniform_num_samples;
163 int input_width, input_height, output_width, output_height;
165 int last_input_width, last_input_height, last_output_width, last_output_height;
166 float last_offset, last_zoom;
167 int src_bilinear_samples, num_loops;
169 Support2DTexture tex;
172 class ResampleComputeEffect : public Effect {
174 // If parent is non-nullptr, calls to inform_input_size will be forwarded,
175 // so that it can inform both passes about the right input and output
177 ResampleComputeEffect(ResampleEffect *parent);
178 ~ResampleComputeEffect();
179 std::string effect_type_id() const override { return "ResampleComputeEffect"; }
181 std::string output_fragment_shader() override;
183 // FIXME: This is the primary reason why this doesn't really work;
184 // there's no good reason why the regular resize should have bounce
185 // but we shouldn't. (If we did a 2D block instead of 1D columns,
186 // it would have been different, but we can't, due to the large size
188 bool needs_texture_bounce() const override { return false; }
189 bool needs_srgb_primaries() const override { return false; }
190 AlphaHandling alpha_handling() const override { return INPUT_PREMULTIPLIED_ALPHA_KEEP_BLANK; }
192 // We specifically do not want mipmaps on the input texture;
193 // they break minification.
194 MipmapRequirements needs_mipmaps() const override { return CANNOT_ACCEPT_MIPMAPS; }
196 void inform_added(EffectChain *chain) override { this->chain = chain; }
197 void inform_input_size(unsigned input_num, unsigned width, unsigned height) override {
198 if (parent != nullptr) {
199 parent->inform_input_size(input_num, width, height);
202 bool changes_output_size() const override { return true; }
203 bool sets_virtual_output_size() const override { return false; }
205 void get_output_size(unsigned *width, unsigned *height, unsigned *virtual_width, unsigned *virtual_height) const override {
206 *virtual_width = *width = this->output_width;
207 *virtual_height = *height = this->output_height;
210 bool is_compute_shader() const override { return true; }
211 void get_compute_dimensions(unsigned output_width, unsigned output_height,
212 unsigned *x, unsigned *y, unsigned *z) const override;
214 void set_gl_state(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num) override;
217 void update_texture(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num);
219 ResampleEffect *parent;
221 Support2DTexture tex_horiz, tex_vert;
222 GLint uniform_sample_tex_horizontal, uniform_sample_tex_vertical;
223 float uniform_num_x_loops;
224 int uniform_num_horizontal_filters, uniform_num_vertical_filters;
225 float uniform_slice_height;
226 float uniform_horizontal_whole_pixel_offset;
227 int uniform_vertical_whole_pixel_offset;
228 int uniform_num_horizontal_samples, uniform_num_vertical_samples;
229 int uniform_output_samples_per_block;
231 int input_width, input_height, output_width, output_height;
232 float offset_x, offset_y, zoom_x, zoom_y;
233 int last_input_width, last_input_height, last_output_width, last_output_height;
234 float last_offset_x, last_offset_y, last_zoom_x, last_zoom_y;
235 int src_horizontal_bilinear_samples; // Horizontal.
236 int src_vertical_samples;
238 float uniform_inv_input_height, uniform_input_texcoord_y_adjust;
239 int uniform_vertical_int_radius;
240 float vertical_scaling_factor;
241 float uniform_inv_vertical_scaling_factor;
246 #endif // !defined(_MOVIT_RESAMPLE_EFFECT_H)