}
}
+// Euclid's algorithm, from Wikipedia.
+unsigned gcd(unsigned a, unsigned b)
+{
+ while (b != 0) {
+ unsigned t = b;
+ b = a % b;
+ a = t;
+ }
+ return a;
+}
+
} // namespace
ResampleEffect::ResampleEffect()
// For horizontal scaling, we fill in the exact same texture;
// the shader just interprets is differently.
//
-// TODO: Support optimization of wrapping the sample texture.
// TODO: Support optimization using free linear sampling, like in BlurEffect.
void SingleResamplePassEffect::update_texture(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num)
{
assert(false);
}
+
+ // For many resamplings (e.g. 640 -> 1280), we will end up with the same
+ // set of samples over and over again in a loop. Thus, we can compute only
+ // the first such loop, and then ask the card to repeat the texture for us.
+ // This is both easier on the texture cache and lowers our CPU cost for
+ // generating the kernel somewhat.
+ num_loops = gcd(src_size, dst_size);
+ slice_height = 1.0f / num_loops;
+ unsigned dst_samples = dst_size / num_loops;
+
// Sample the kernel in the right place. A diagram with a triangular kernel
// (corresponding to linear filtering, and obviously with radius 1)
// for easier ASCII art drawing:
float radius_scaling_factor = std::min(float(dst_size) / float(src_size), 1.0f);
int int_radius = lrintf(LANCZOS_RADIUS / radius_scaling_factor);
src_samples = int_radius * 2 + 1;
- float *weights = new float[dst_size * src_samples * 2];
- for (unsigned y = 0; y < dst_size; ++y) {
+ float *weights = new float[dst_samples * src_samples * 2];
+ 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) * float(src_size) / float(dst_size) - 0.5f;
}
}
- // Encode as a two-component texture. Note the GL_REPEAT, which is not relevant
- // right now, but will be later.
+ // Encode as a two-component texture. Note the GL_REPEAT.
glActiveTexture(GL_TEXTURE0 + *sampler_num);
check_error();
glBindTexture(GL_TEXTURE_2D, texnum);
check_error();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
check_error();
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, src_samples, dst_size, 0, GL_RG, GL_FLOAT, weights);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, src_samples, dst_samples, 0, GL_RG, GL_FLOAT, weights);
check_error();
delete[] weights;
-
}
void SingleResamplePassEffect::set_gl_state(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num)
set_uniform_int(glsl_program_num, prefix, "sample_tex", *sampler_num);
++sampler_num;
set_uniform_int(glsl_program_num, prefix, "num_samples", src_samples);
+ set_uniform_float(glsl_program_num, prefix, "num_loops", num_loops);
+ set_uniform_float(glsl_program_num, prefix, "slice_height", slice_height);
// Instructions for how to convert integer sample numbers to positions in the weight texture.
set_uniform_float(glsl_program_num, prefix, "sample_x_scale", 1.0f / src_samples);
uniform sampler2D PREFIX(sample_tex);
uniform int PREFIX(num_samples);
+uniform float PREFIX(num_loops);
uniform float PREFIX(sample_x_scale);
uniform float PREFIX(sample_x_offset);
+uniform float PREFIX(slice_height);
// Sample a single weight. First fetch information about where to sample
// and the weight from sample_tex, and then read the pixel itself.
vec2 sample_tc;
sample_tc.x = float(i) * PREFIX(sample_x_scale) + PREFIX(sample_x_offset);
#if DIRECTION_VERTICAL
- sample_tc.y = tc.y;
+ sample_tc.y = tc.y * PREFIX(num_loops);
#else
- sample_tc.y = tc.x;
+ sample_tc.y = tc.x * PREFIX(num_loops);
#endif
vec2 sample = texture2D(PREFIX(sample_tex), sample_tc).rg;
#if DIRECTION_VERTICAL
- tc.y = sample.g;
+ tc.y = sample.g + floor(sample_tc.y) * PREFIX(slice_height);
#else
- tc.x = sample.g;
+ tc.x = sample.g + floor(sample_tc.y) * PREFIX(slice_height);
#endif
return vec4(sample.r) * INPUT(tc);
}