/* Scaling library v1.0 by fishku Copyright (C) 2023 Public domain license (CC0) This file acts like a library and should be included in another shader to be used. For example usages, see the border/blur_fill shaders. It's recommended to use these functions in the vertex shader pass, and pass the data to the fragment pass. Features: - Cropping on each side - Centering of image after crop has been applied - Forcing of a certain aspect ratio - Forcing of either vert. or horiz. integer scaling, or both - Rotation support (0, 90, 180, 270 degrees). Refactored from the version that used to be in the blur_fill shader. Changelog: v1.0: Initial conversion from blur_fill release. Add rotation support. */ vec4 get_rotated_crop(vec4 crop, uint rotation) { switch (rotation) { case 0: default: return crop; case 1: return crop.yzwx; case 2: return crop.zwxy; case 3: return crop.wxyz; } } vec2 get_rotated_size(vec2 x, uint rotation) { switch (rotation) { case 0: case 2: default: return x; case 1: case 3: return x.yx; } } // Get 2 corners of input in texel space, spanning the input image. // corners.x and .y define the top-left corner, corners.z and .w define the // bottom-right corner. vec4 get_input_corners(vec2 input_size, vec4 crop, uint rotation) { crop = get_rotated_crop(crop, rotation); return vec4(crop.y, crop.x, input_size.x - crop.w, input_size.y - crop.z); } // Get adjusted center in input pixel coordinate system. vec2 get_input_center(vec2 input_size, vec4 crop, uint rotation, float center_after_cropping) { crop = get_rotated_crop(crop, rotation); return center_after_cropping > 0.5 ? 0.5 * vec2(crop.y + input_size.x - crop.w, crop.x + input_size.y - crop.z) : vec2(0.49999) * input_size; } // Scaling from unit output to pixel input space. vec2 get_scale_o2i(vec2 input_size, vec2 output_size, vec4 crop, uint rotation, float center_after_cropping, float force_aspect_ratio, vec2 aspect, vec2 force_integer_scaling, bool output_size_is_final_viewport_size) { crop = get_rotated_crop(crop, rotation); if (output_size_is_final_viewport_size) { output_size = get_rotated_size(output_size, rotation); } aspect = get_rotated_size(aspect, rotation); // Aspect ratio before cropping. // lambda_1 * input_pixels.x, lambda_2 * input_pixels.y, // possibly corrected for forced aspect ratio aspect = (force_aspect_ratio < 0.5 ? output_size * input_size.yx : (aspect.x < 0.5 || aspect.y < 0.5 ? vec2(1.0) : vec2(aspect.x, aspect.y) * input_size.yx)); // Pixels in input coord. space, after cropping. input_size = input_size - (center_after_cropping > 0.5 ? vec2(crop.y + crop.w, crop.x + crop.z) : 2.0 * vec2(min(crop.y, crop.w), min(crop.x, crop.z))); force_integer_scaling = get_rotated_size(force_integer_scaling, rotation); float scale_x, scale_y; if (output_size.x / (input_size.x * aspect.x) < output_size.y / (input_size.y * aspect.y)) { // Scale will be limited by width. Calc x scale, then derive y scale // using aspect ratio. scale_x = output_size.x / input_size.x; if (force_integer_scaling.x > 0.5 && scale_x > 1.0) { scale_x = floor(scale_x); } scale_y = scale_x * aspect.y / aspect.x; if (force_integer_scaling.y > 0.5 && scale_y > 1.0) { scale_y = floor(scale_y); } } else { // Scale will be limited by height. scale_y = output_size.y / input_size.y; if (force_integer_scaling.y > 0.5 && scale_y > 1.0) { scale_y = floor(scale_y); } scale_x = scale_y * aspect.x / aspect.y; if (force_integer_scaling.x > 0.5 && scale_x > 1.0) { scale_x = floor(scale_x); } } return output_size.xy / vec2(scale_x, scale_y); } // From unit output to pixel input space. // coord_in_input_space = o2i(coord_in_output_space) // This is used to sample from the input texture in the output pass. // Version where scale is passed in. vec2 o2i(vec2 x, vec2 input_size, vec4 crop, uint rotation, float center_after_cropping, vec2 scale_o2i) { return (x - 0.49999) * scale_o2i + get_input_center(input_size, crop, rotation, center_after_cropping); } // Version that computes scale. vec2 o2i(vec2 x, vec2 input_size, vec2 output_size, vec4 crop, uint rotation, float center_after_cropping, float force_aspect_ratio, vec2 aspect, vec2 force_integer_scaling, bool output_size_is_final_viewport_size) { return o2i(x, input_size, crop, rotation, center_after_cropping, get_scale_o2i(input_size, output_size, crop, rotation, center_after_cropping, force_aspect_ratio, aspect, force_integer_scaling, output_size_is_final_viewport_size)); } // From pixel input to unit output space. // Version where scale is passed in. vec2 i2o(vec2 x, vec2 input_size, vec4 crop, uint rotation, float center_after_cropping, vec2 scale_o2i) { return (x - get_input_center(input_size, crop, rotation, center_after_cropping)) / scale_o2i + 0.49999; } // Version that computes scale. vec2 i2o(vec2 x, vec2 input_size, vec2 output_size, vec4 crop, uint rotation, float center_after_cropping, float force_aspect_ratio, vec2 aspect, vec2 force_integer_scaling, bool output_size_is_final_viewport_size) { return (x - get_input_center(input_size, crop, rotation, center_after_cropping)) / get_scale_o2i(input_size, output_size, crop, rotation, center_after_cropping, force_aspect_ratio, aspect, force_integer_scaling, output_size_is_final_viewport_size) + 0.49999; }