1258 lines · 45.5 KB · Text
RawDownload
#include "alpha_correction.hlsl" cbuffer GlobalParams: register(b0) {    float4 gamma_ratios;    float2 global_viewport_size;    float grayscale_enhanced_contrast;    float subpixel_enhanced_contrast;    uint is_bgr;    uint3 global_pad;}; Texture2D<float4> t_sprite: register(t0);SamplerState s_sprite: register(s0); struct SubpixelSpriteFragmentOutput {    float4 foreground : SV_Target0;    float4 alpha : SV_Target1;}; struct Bounds {    float2 origin;    float2 size;}; struct Corners {    float top_left;    float top_right;    float bottom_right;    float bottom_left;}; struct Edges {    float top;    float right;    float bottom;    float left;}; struct Hsla {    float h;    float s;    float l;    float a;}; struct LinearColorStop {    Hsla color;    float percentage;}; struct Background {    // 0u is Solid    // 1u is LinearGradient    // 2u is PatternSlash    uint tag;    // 0u is sRGB linear color    // 1u is Oklab color    uint color_space;    Hsla solid;    float gradient_angle_or_pattern_height;    LinearColorStop colors[2];    uint pad;}; struct GradientColor {  float4 solid;  float4 color0;  float4 color1;}; struct AtlasTextureId {    uint index;    uint kind;}; struct AtlasBounds {    int2 origin;    int2 size;}; struct AtlasTile {    AtlasTextureId texture_id;    uint tile_id;    uint padding;    AtlasBounds bounds;}; struct TransformationMatrix {    float2x2 rotation_scale;    float2 translation;}; static const float M_PI_F = 3.141592653f;static const float3 GRAYSCALE_FACTORS = float3(0.2126f, 0.7152f, 0.0722f); float4 to_device_position_impl(float2 position) {    float2 device_position = position / global_viewport_size * float2(2.0, -2.0) + float2(-1.0, 1.0);    return float4(device_position, 0., 1.);} float4 to_device_position(float2 unit_vertex, Bounds bounds) {    float2 position = unit_vertex * bounds.size + bounds.origin;    return to_device_position_impl(position);} float4 distance_from_clip_rect_impl(float2 position, Bounds clip_bounds) {    float2 tl = position - clip_bounds.origin;    float2 br = clip_bounds.origin + clip_bounds.size - position;    return float4(tl.x, br.x, tl.y, br.y);} float4 distance_from_clip_rect(float2 unit_vertex, Bounds bounds, Bounds clip_bounds) {    float2 position = unit_vertex * bounds.size + bounds.origin;    return distance_from_clip_rect_impl(position, clip_bounds);} float4 distance_from_clip_rect_transformed(float2 unit_vertex, Bounds bounds, Bounds clip_bounds, TransformationMatrix transformation) {    float2 position = unit_vertex * bounds.size + bounds.origin;    float2 transformed = mul(position, transformation.rotation_scale) + transformation.translation;    return distance_from_clip_rect_impl(transformed, clip_bounds);} // Convert linear RGB to sRGBfloat3 linear_to_srgb(float3 color) {    return pow(color, float3(2.2, 2.2, 2.2));} // Convert sRGB to linear RGBfloat3 srgb_to_linear(float3 color) {    return pow(color, float3(1.0 / 2.2, 1.0 / 2.2, 1.0 / 2.2));} /// Hsla to linear RGBA conversion.float4 hsla_to_rgba(Hsla hsla) {    float h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range    float s = hsla.s;    float l = hsla.l;    float a = hsla.a;     float c = (1.0 - abs(2.0 * l - 1.0)) * s;    float x = c * (1.0 - abs(fmod(h, 2.0) - 1.0));    float m = l - c / 2.0;     float r = 0.0;    float g = 0.0;    float b = 0.0;     if (h >= 0.0 && h < 1.0) {        r = c;        g = x;        b = 0.0;    } else if (h >= 1.0 && h < 2.0) {        r = x;        g = c;        b = 0.0;    } else if (h >= 2.0 && h < 3.0) {        r = 0.0;        g = c;        b = x;    } else if (h >= 3.0 && h < 4.0) {        r = 0.0;        g = x;        b = c;    } else if (h >= 4.0 && h < 5.0) {        r = x;        g = 0.0;        b = c;    } else {        r = c;        g = 0.0;        b = x;    }     float4 rgba;    rgba.x = (r + m);    rgba.y = (g + m);    rgba.z = (b + m);    rgba.w = a;    return rgba;} // Converts a sRGB color to the Oklab color space.// Reference: https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklabfloat4 srgb_to_oklab(float4 color) {    // Convert non-linear sRGB to linear sRGB    color = float4(srgb_to_linear(color.rgb), color.a);     float l = 0.4122214708 * color.r + 0.5363325363 * color.g + 0.0514459929 * color.b;    float m = 0.2119034982 * color.r + 0.6806995451 * color.g + 0.1073969566 * color.b;    float s = 0.0883024619 * color.r + 0.2817188376 * color.g + 0.6299787005 * color.b;     float l_ = pow(l, 1.0/3.0);    float m_ = pow(m, 1.0/3.0);    float s_ = pow(s, 1.0/3.0);     return float4(        0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_,        1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_,        0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_,        color.a    );} // Converts an Oklab color to the sRGB color space.float4 oklab_to_srgb(float4 color) {    float l_ = color.r + 0.3963377774 * color.g + 0.2158037573 * color.b;    float m_ = color.r - 0.1055613458 * color.g - 0.0638541728 * color.b;    float s_ = color.r - 0.0894841775 * color.g - 1.2914855480 * color.b;     float l = l_ * l_ * l_;    float m = m_ * m_ * m_;    float s = s_ * s_ * s_;     float3 linear_rgb = float3(        4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,        -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,        -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s    );     // Convert linear sRGB to non-linear sRGB    return float4(linear_to_srgb(linear_rgb), color.a);} // This approximates the error function, needed for the gaussian integralfloat2 erf(float2 x) {    float2 s = sign(x);    float2 a = abs(x);    x = 1. + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;    x *= x;    return s - s / (x * x);} float blur_along_x(float x, float y, float sigma, float corner, float2 half_size) {    float delta = min(half_size.y - corner - abs(y), 0.);    float curved = half_size.x - corner + sqrt(max(0., corner * corner - delta * delta));    float2 integral = 0.5 + 0.5 * erf((x + float2(-curved, curved)) * (sqrt(0.5) / sigma));    return integral.y - integral.x;} // A standard gaussian function, used for weighting samplesfloat gaussian(float x, float sigma) {    return exp(-(x * x) / (2. * sigma * sigma)) / (sqrt(2. * M_PI_F) * sigma);} float4 over(float4 below, float4 above) {    float4 result;    float alpha = above.a + below.a * (1.0 - above.a);    result.rgb = (above.rgb * above.a + below.rgb * below.a * (1.0 - above.a)) / alpha;    result.a = alpha;    return result;} float2 to_tile_position(float2 unit_vertex, AtlasTile tile) {    float2 atlas_size;    t_sprite.GetDimensions(atlas_size.x, atlas_size.y);    return (float2(tile.bounds.origin) + unit_vertex * float2(tile.bounds.size)) / atlas_size;} // Selects corner radius based on quadrant.float pick_corner_radius(float2 center_to_point, Corners corner_radii) {    if (center_to_point.x < 0.) {        if (center_to_point.y < 0.) {            return corner_radii.top_left;        } else {            return corner_radii.bottom_left;        }    } else {        if (center_to_point.y < 0.) {            return corner_radii.top_right;        } else {            return corner_radii.bottom_right;        }    }} float4 to_device_position_transformed(float2 unit_vertex, Bounds bounds,                                      TransformationMatrix transformation) {    float2 position = unit_vertex * bounds.size + bounds.origin;    float2 transformed = mul(position, transformation.rotation_scale) + transformation.translation;    float2 device_position = transformed / global_viewport_size * float2(2.0, -2.0) + float2(-1.0, 1.0);    return float4(device_position, 0.0, 1.0);} // Implementation of quad signed distance fieldfloat quad_sdf_impl(float2 corner_center_to_point, float corner_radius) {    if (corner_radius == 0.0) {        // Fast path for unrounded corners        return max(corner_center_to_point.x, corner_center_to_point.y);    } else {        // Signed distance of the point from a quad that is inset by corner_radius        // It is negative inside this quad, and positive outside        float signed_distance_to_inset_quad =            // 0 inside the inset quad, and positive outside            length(max(float2(0.0, 0.0), corner_center_to_point)) +            // 0 outside the inset quad, and negative inside            min(0.0, max(corner_center_to_point.x, corner_center_to_point.y));         return signed_distance_to_inset_quad - corner_radius;    }} float quad_sdf(float2 pt, Bounds bounds, Corners corner_radii) {    float2 half_size = bounds.size / 2.;    float2 center = bounds.origin + half_size;    float2 center_to_point = pt - center;    float corner_radius = pick_corner_radius(center_to_point, corner_radii);    float2 corner_to_point = abs(center_to_point) - half_size;    float2 corner_center_to_point = corner_to_point + corner_radius;    return quad_sdf_impl(corner_center_to_point, corner_radius);} GradientColor prepare_gradient_color(uint tag, uint color_space, Hsla solid, LinearColorStop colors[2]) {    GradientColor output;    if (tag == 0 || tag == 2 || tag == 3) {        output.solid = hsla_to_rgba(solid);    } else if (tag == 1) {        output.color0 = hsla_to_rgba(colors[0].color);        output.color1 = hsla_to_rgba(colors[1].color);         // Prepare color space in vertex for avoid conversion        // in fragment shader for performance reasons        if (color_space == 1) {            // Oklab            output.color0 = srgb_to_oklab(output.color0);            output.color1 = srgb_to_oklab(output.color1);        }    }     return output;} float2x2 rotate2d(float angle) {    float s = sin(angle);    float c = cos(angle);    return float2x2(c, -s, s, c);} float4 gradient_color(Background background,                      float2 position,                      Bounds bounds,                      float4 solid_color, float4 color0, float4 color1) {    float4 color;     switch (background.tag) {        case 0:            color = solid_color;            break;        case 1: {            // -90 degrees to match the CSS gradient angle.            float gradient_angle = background.gradient_angle_or_pattern_height;            float radians = (fmod(gradient_angle, 360.0) - 90.0) * (M_PI_F / 180.0);            float2 direction = float2(cos(radians), sin(radians));             // Expand the short side to be the same as the long side            if (bounds.size.x > bounds.size.y) {                direction.y *= bounds.size.y / bounds.size.x;            } else {                direction.x *=  bounds.size.x / bounds.size.y;            }             // Get the t value for the linear gradient with the color stop percentages.            float2 half_size = bounds.size * 0.5;            float2 center = bounds.origin + half_size;            float2 center_to_point = position - center;            float t = dot(center_to_point, direction) / length(direction);            // Check the direct to determine the use x or y            if (abs(direction.x) > abs(direction.y)) {                t = (t + half_size.x) / bounds.size.x;            } else {                t = (t + half_size.y) / bounds.size.y;            }             // Adjust t based on the stop percentages            t = (t - background.colors[0].percentage)                / (background.colors[1].percentage                - background.colors[0].percentage);            t = clamp(t, 0.0, 1.0);             switch (background.color_space) {                case 0:                    color = lerp(color0, color1, t);                    break;                case 1: {                    float4 oklab_color = lerp(color0, color1, t);                    color = oklab_to_srgb(oklab_color);                    break;                }            }             // Dither to reduce banding in gradients (especially dark/alpha).            // Triangular-distributed noise breaks up 8-bit quantization steps.            // ±2/255 for RGB (enough for dark-on-dark compositing),            // ±3/255 for alpha (needs more because alpha × dark color = tiny steps).            {                float2 seed = position * 0.6180339887; // golden ratio spread                float r1 = frac(sin(dot(seed, float2(12.9898, 78.233))) * 43758.5453);                float r2 = frac(sin(dot(seed, float2(39.3460, 11.135))) * 24634.6345);                float tri = r1 + r2 - 1.0; // triangular PDF, range [-1, +1]                color.rgb += tri * 2.0 / 255.0;                color.a   += tri * 3.0 / 255.0;            }             break;        }        case 2: {            float gradient_angle_or_pattern_height = background.gradient_angle_or_pattern_height;            float pattern_width = (gradient_angle_or_pattern_height / 65535.0f) / 255.0f;            float pattern_interval = fmod(gradient_angle_or_pattern_height, 65535.0f) / 255.0f;            float pattern_height = pattern_width + pattern_interval;            float stripe_angle = M_PI_F / 4.0;            float pattern_period = pattern_height * sin(stripe_angle);            float2x2 rotation = rotate2d(stripe_angle);            float2 relative_position = position - bounds.origin;            float2 rotated_point = mul(relative_position, rotation);            float pattern = fmod(rotated_point.x, pattern_period);            float distance = min(pattern, pattern_period - pattern) - pattern_period * (pattern_width / pattern_height) /  2.0f;            color = solid_color;            color.a *= saturate(0.5 - distance);            break;        }        case 3: {            // checkerboard            float size = background.gradient_angle_or_pattern_height;            float2 relative_position = position - bounds.origin;             float x_index = floor(relative_position.x / size);            float y_index = floor(relative_position.y / size);            float should_be_colored = (x_index + y_index) % 2.0;             color = solid_color;            color.a *= saturate(should_be_colored);            break;        }    }     return color;} // Returns the dash velocity of a corner given the dash velocity of the two// sides, by returning the slower velocity (larger dashes).//// Since 0 is used for dash velocity when the border width is 0 (instead of// +inf), this returns the other dash velocity in that case.//// An alternative to this might be to appropriately interpolate the dash// velocity around the corner, but that seems overcomplicated.float corner_dash_velocity(float dv1, float dv2) {    if (dv1 == 0.0) {        return dv2;    } else if (dv2 == 0.0) {        return dv1;    } else {        return min(dv1, dv2);    }} // Returns alpha used to render antialiased dashes.// `t` is within the dash when `fmod(t, period) < length`.float dash_alpha(    float t, float period, float length, float dash_velocity,    float antialias_threshold) {    float half_period = period / 2.0;    float half_length = length / 2.0;    // Value in [-half_period, half_period]    // The dash is in [-half_length, half_length]    float centered = fmod(t + half_period - half_length, period) - half_period;    // Signed distance for the dash, negative values are inside the dash    float signed_distance = abs(centered) - half_length;    // Antialiased alpha based on the signed distance    return saturate(antialias_threshold - signed_distance / dash_velocity);} // This approximates distance to the nearest point to a quarter ellipse in a way// that is sufficient for anti-aliasing when the ellipse is not very eccentric.// The components of `point` are expected to be positive.//// Negative on the outside and positive on the inside.float quarter_ellipse_sdf(float2 pt, float2 radii) {    // Scale the space to treat the ellipse like a unit circle    float2 circle_vec = pt / radii;    float unit_circle_sdf = length(circle_vec) - 1.0;    // Approximate up-scaling of the length by using the average of the radii.    //    // TODO: A better solution would be to use the gradient of the implicit    // function for an ellipse to approximate a scaling factor.    return unit_circle_sdf * (radii.x + radii.y) * -0.5;} /*****              Quads***/ struct Quad {    uint order;    uint border_style;    Bounds bounds;    Bounds content_mask;

Lines 1–500 of 1258.