/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifdef WR_FRAGMENT_SHADER // One iteration of Newton's method on the 2D equation of an ellipse: // // E(x, y) = x^2/a^2 + y^2/b^2 - 1 // // The Jacobian of this equation is: // // J(E(x, y)) = [ 2*x/a^2 2*y/b^2 ] // // We approximate the distance with: // // E(x, y) / ||J(E(x, y))|| // // See G. Taubin, "Distance Approximations for Rasterizing Implicit // Curves", section 3. float distance_to_ellipse(vec2 p, vec2 radii, float aa_range) { float dist; if (any(lessThanEqual(radii, vec2(0.0)))) { dist = length(p); } else { vec2 invRadiiSq = 1.0 / (radii * radii); float g = dot(p * p * invRadiiSq, vec2(1.0)) - 1.0; vec2 dG = 2.0 * p * invRadiiSq; dist = g * inversesqrt(dot(dG, dG)); } return clamp(dist, -aa_range, aa_range); } float clip_against_ellipse_if_needed( vec2 pos, float current_distance, vec4 ellipse_center_radius, vec2 sign_modifier, float aa_range ) { if (!all(lessThan(sign_modifier * pos, sign_modifier * ellipse_center_radius.xy))) { return current_distance; } float distance = distance_to_ellipse(pos - ellipse_center_radius.xy, ellipse_center_radius.zw, aa_range); return max(distance, current_distance); } float rounded_rect(vec2 pos, vec4 clip_center_radius_tl, vec4 clip_center_radius_tr, vec4 clip_center_radius_br, vec4 clip_center_radius_bl, float aa_range) { // Start with a negative value (means "inside") for all fragments that are not // in a corner. If the fragment is in a corner, one of the clip_against_ellipse_if_needed // calls below will update it. float current_distance = -aa_range; // Clip against each ellipse. current_distance = clip_against_ellipse_if_needed(pos, current_distance, clip_center_radius_tl, vec2(1.0), aa_range); current_distance = clip_against_ellipse_if_needed(pos, current_distance, clip_center_radius_tr, vec2(-1.0, 1.0), aa_range); current_distance = clip_against_ellipse_if_needed(pos, current_distance, clip_center_radius_br, vec2(-1.0), aa_range); current_distance = clip_against_ellipse_if_needed(pos, current_distance, clip_center_radius_bl, vec2(1.0, -1.0), aa_range); // Apply AA // See comment in ps_border_corner about the choice of constants. return distance_aa(aa_range, current_distance); } #endif