]> git.sesse.net Git - movit/blobdiff - resample_effect.cpp
Refactor calculate_scaling_weights() so that it is possible to get weights without...
[movit] / resample_effect.cpp
index ffc64fb7f76a2828c3d182f7cdd8cf73c11cd186..802a8968a893482460988b910f2db707d68b692b 100644 (file)
@@ -300,8 +300,7 @@ double compute_sum_sq_error(const Tap<float>* weights, unsigned num_weights,
 }  // namespace
 
 ResampleEffect::ResampleEffect()
-       : owns_effects(true),
-         input_width(1280),
+       : input_width(1280),
          input_height(720),
          offset_x(0.0f), offset_y(0.0f),
          zoom_x(1.0f), zoom_y(1.0f),
@@ -311,9 +310,11 @@ ResampleEffect::ResampleEffect()
        register_int("height", &output_height);
 
        // The first blur pass will forward resolution information to us.
-       hpass = new SingleResamplePassEffect(this);
+       hpass_owner.reset(new SingleResamplePassEffect(this));
+       hpass = hpass_owner.get();
        CHECK(hpass->set_int("direction", SingleResamplePassEffect::HORIZONTAL));
-       vpass = new SingleResamplePassEffect(nullptr);
+       vpass_owner.reset(new SingleResamplePassEffect(this));
+       vpass = vpass_owner.get();
        CHECK(vpass->set_int("direction", SingleResamplePassEffect::VERTICAL));
 
        update_size();
@@ -321,21 +322,16 @@ ResampleEffect::ResampleEffect()
 
 ResampleEffect::~ResampleEffect()
 {
-       if (owns_effects) {
-               delete hpass;
-               delete vpass;
-       }
 }
 
 void ResampleEffect::rewrite_graph(EffectChain *graph, Node *self)
 {
-       Node *hpass_node = graph->add_node(hpass);
-       Node *vpass_node = graph->add_node(vpass);
+       Node *hpass_node = graph->add_node(hpass_owner.release());
+       Node *vpass_node = graph->add_node(vpass_owner.release());
        graph->connect_nodes(hpass_node, vpass_node);
        graph->replace_receiver(self, hpass_node);
        graph->replace_sender(self, vpass_node);
        self->disabled = true;
-       owns_effects = false;
 } 
 
 // We get this information forwarded from the first blur pass,
@@ -439,8 +435,8 @@ bool ResampleEffect::set_float(const string &key, float value) {
 SingleResamplePassEffect::SingleResamplePassEffect(ResampleEffect *parent)
        : parent(parent),
          direction(HORIZONTAL),
-         input_width(1280),
-         input_height(720),
+         input_width(1280),
+         input_height(720),
          offset(0.0),
          zoom(1.0),
          last_input_width(-1),
@@ -506,7 +502,7 @@ void SingleResamplePassEffect::update_texture(GLuint glsl_program_num, const str
                assert(false);
        }
 
-       ScalingWeights weights = calculate_scaling_weights(src_size, dst_size, zoom, offset);
+       ScalingWeights weights = calculate_bilinear_scaling_weights(src_size, dst_size, zoom, offset);
        src_bilinear_samples = weights.src_bilinear_samples;
        num_loops = weights.num_loops;
        slice_height = 1.0f / weights.num_loops;
@@ -533,6 +529,8 @@ void SingleResamplePassEffect::update_texture(GLuint glsl_program_num, const str
        tex.update(weights.src_bilinear_samples, weights.dst_samples, internal_format, GL_RG, type, pixels);
 }
 
+namespace {
+
 ScalingWeights calculate_scaling_weights(unsigned src_size, unsigned dst_size, float zoom, float offset)
 {
        // Only needed if run from outside ResampleEffect.
@@ -609,15 +607,16 @@ ScalingWeights calculate_scaling_weights(unsigned src_size, unsigned dst_size, f
        // to compute the destination pixel, and how many depend on the scaling factor.
        // Thus, the kernel width will vary with how much we scale.
        float radius_scaling_factor = min(scaling_factor, 1.0f);
-       int int_radius = lrintf(LANCZOS_RADIUS / radius_scaling_factor);
-       int src_samples = int_radius * 2 + 1;
+       const int int_radius = lrintf(LANCZOS_RADIUS / radius_scaling_factor);
+       const int src_samples = int_radius * 2 + 1;
        unique_ptr<Tap<float>[]> weights(new Tap<float>[dst_samples * src_samples]);
        float subpixel_offset = offset - lrintf(offset);  // The part not covered by whole_pixel_offset.
        assert(subpixel_offset >= -0.5f && subpixel_offset <= 0.5f);
+       float inv_scaling_factor = 1.0f / scaling_factor;
        for (unsigned y = 0; y < dst_samples; ++y) {
                // Find the point around which we want to sample the source image,
                // compensating for differing pixel centers as the scale changes.
-               float center_src_y = (y + 0.5f) scaling_factor - 0.5f;
+               float center_src_y = (y + 0.5f) * inv_scaling_factor - 0.5f;
                int base_src_y = lrintf(center_src_y);
 
                // Now sample <int_radius> pixels on each side around that point.
@@ -630,16 +629,33 @@ ScalingWeights calculate_scaling_weights(unsigned src_size, unsigned dst_size, f
                }
        }
 
+       ScalingWeights ret;
+       ret.src_bilinear_samples = src_samples;
+       ret.dst_samples = dst_samples;
+       ret.num_loops = num_loops;
+       ret.bilinear_weights_fp16 = nullptr;
+       ret.bilinear_weights_fp32 = move(weights);
+       return ret;
+}
+
+}  // namespace
+
+ScalingWeights calculate_bilinear_scaling_weights(unsigned src_size, unsigned dst_size, float zoom, float offset)
+{
+       ScalingWeights ret = calculate_scaling_weights(src_size, dst_size, zoom, offset);
+       unique_ptr<Tap<float>[]> weights = move(ret.bilinear_weights_fp32);
+       const int src_samples = ret.src_bilinear_samples;
+
        // Now make use of the bilinear filtering in the GPU to reduce the number of samples
        // we need to make. Try fp16 first; if it's not accurate enough, we go to fp32.
        // Our tolerance level for total error is a bit higher than the one for invididual
        // samples, since one would assume overall errors in the shape don't matter as much.
        const float max_error = 2.0f / (255.0f * 255.0f);
        unique_ptr<Tap<fp16_int_t>[]> bilinear_weights_fp16;
-       int src_bilinear_samples = combine_many_samples(weights.get(), src_size, src_samples, dst_samples, &bilinear_weights_fp16);
+       int src_bilinear_samples = combine_many_samples(weights.get(), src_size, src_samples, ret.dst_samples, &bilinear_weights_fp16);
        unique_ptr<Tap<float>[]> bilinear_weights_fp32 = nullptr;
        double max_sum_sq_error_fp16 = 0.0;
-       for (unsigned y = 0; y < dst_samples; ++y) {
+       for (unsigned y = 0; y < ret.dst_samples; ++y) {
                double sum_sq_error_fp16 = compute_sum_sq_error(
                        weights.get() + y * src_samples, src_samples,
                        bilinear_weights_fp16.get() + y * src_bilinear_samples, src_bilinear_samples,
@@ -652,13 +668,10 @@ ScalingWeights calculate_scaling_weights(unsigned src_size, unsigned dst_size, f
 
        if (max_sum_sq_error_fp16 > max_error) {
                bilinear_weights_fp16.reset();
-               src_bilinear_samples = combine_many_samples(weights.get(), src_size, src_samples, dst_samples, &bilinear_weights_fp32);
+               src_bilinear_samples = combine_many_samples(weights.get(), src_size, src_samples, ret.dst_samples, &bilinear_weights_fp32);
        }
 
-       ScalingWeights ret;
        ret.src_bilinear_samples = src_bilinear_samples;
-       ret.dst_samples = dst_samples;
-       ret.num_loops = num_loops;
        ret.bilinear_weights_fp16 = move(bilinear_weights_fp16);
        ret.bilinear_weights_fp32 = move(bilinear_weights_fp32);
        return ret;