2023-01-10 01:07:06 +00:00
/*
* Copyright ( c ) 2022 - 2023 , MacDue < macdue @ dueutil . tech >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <AK/Math.h>
2024-07-05 08:40:29 +00:00
# include <LibGfx/DeprecatedPainter.h>
2023-01-10 01:07:06 +00:00
# include <LibGfx/Gradients.h>
2023-01-15 22:10:36 +00:00
# include <LibGfx/PaintStyle.h>
2023-01-10 01:07:06 +00:00
# if defined(AK_COMPILER_GCC)
# pragma GCC optimize("O3")
# endif
2023-01-15 22:10:36 +00:00
2023-01-10 01:07:06 +00:00
namespace Gfx {
2023-01-17 19:52:02 +00:00
// Note: This file implements the CSS/Canvas gradients for LibWeb according to the spec.
2023-01-10 01:07:06 +00:00
// Please do not make ad-hoc changes that may break spec compliance!
2024-07-10 13:54:49 +00:00
float color_stop_step ( ColorStop const & previous_stop , ColorStop const & next_stop , float position )
2023-01-10 01:07:06 +00:00
{
if ( position < previous_stop . position )
return 0 ;
if ( position > next_stop . position )
return 1 ;
// For any given point between the two color stops,
// determine the point’ s location as a percentage of the distance between the two color stops.
// Let this percentage be P.
auto stop_length = next_stop . position - previous_stop . position ;
// FIXME: Avoids NaNs... Still not quite correct?
if ( stop_length < = 0 )
return 1 ;
auto p = ( position - previous_stop . position ) / stop_length ;
if ( ! next_stop . transition_hint . has_value ( ) )
return p ;
if ( * next_stop . transition_hint > = 1 )
return 0 ;
if ( * next_stop . transition_hint < = 0 )
return 1 ;
// Let C, the color weighting at that point, be equal to P^(logH(.5)).
auto c = AK : : pow ( p , AK : : log < float > ( 0.5 ) / AK : : log ( * next_stop . transition_hint ) ) ;
// The color at that point is then a linear blend between the colors of the two color stops,
// blending (1 - C) of the first stop and C of the second stop.
return c ;
}
class GradientLine {
public :
2024-08-02 09:37:45 +00:00
GradientLine ( int gradient_length , ReadonlySpan < ColorStop > color_stops , Optional < float > repeat_length , AlphaType alpha_type = AlphaType : : Premultiplied )
2023-08-19 18:12:00 +00:00
: m_repeat_mode ( repeat_length . has_value ( ) ? RepeatMode : : Repeat : RepeatMode : : None )
, m_start_offset ( round_to < int > ( ( repeating ( ) ? color_stops . first ( ) . position : 0.0f ) * gradient_length ) )
2023-01-29 13:29:55 +00:00
, m_color_stops ( color_stops )
2024-08-02 09:37:45 +00:00
, m_alpha_type ( alpha_type )
2023-01-10 01:07:06 +00:00
{
// Avoid generating excessive amounts of colors when the not enough shades to fill that length.
auto necessary_length = min < int > ( ( color_stops . size ( ) - 1 ) * 255 , gradient_length ) ;
m_sample_scale = float ( necessary_length ) / gradient_length ;
// Note: color_count will be < gradient_length for repeating gradients.
auto color_count = round_to < int > ( repeat_length . value_or ( 1.0f ) * necessary_length ) ;
m_gradient_line_colors . resize ( color_count ) ;
2023-01-15 22:10:36 +00:00
2023-01-10 01:07:06 +00:00
for ( int loc = 0 ; loc < color_count ; loc + + ) {
auto relative_loc = float ( loc + m_start_offset ) / necessary_length ;
2023-01-15 22:10:36 +00:00
Color gradient_color = color_blend ( color_stops [ 0 ] . color , color_stops [ 1 ] . color ,
2023-01-10 01:07:06 +00:00
color_stop_step ( color_stops [ 0 ] , color_stops [ 1 ] , relative_loc ) ) ;
for ( size_t i = 1 ; i < color_stops . size ( ) - 1 ; i + + ) {
2023-01-15 22:10:36 +00:00
gradient_color = color_blend ( gradient_color , color_stops [ i + 1 ] . color ,
2023-01-10 01:07:06 +00:00
color_stop_step ( color_stops [ i ] , color_stops [ i + 1 ] , relative_loc ) ) ;
}
m_gradient_line_colors [ loc ] = gradient_color ;
if ( gradient_color . alpha ( ) < 255 )
m_requires_blending = true ;
}
}
2023-01-23 00:35:57 +00:00
Color color_blend ( Color a , Color b , float amount ) const
{
// Note: color.mixed_with() performs premultiplied alpha mixing when necessary as defined in:
// https://drafts.csswg.org/css-images/#coloring-gradient-line
2024-08-02 09:37:45 +00:00
if ( m_alpha_type = = AlphaType : : Premultiplied )
2023-01-23 00:35:57 +00:00
return a . mixed_with ( b , amount ) ;
return a . interpolate ( b , amount ) ;
2023-07-08 02:48:11 +00:00
}
2023-01-23 00:35:57 +00:00
2023-01-10 01:07:06 +00:00
Color get_color ( i64 index ) const
{
2023-01-23 00:35:57 +00:00
if ( index < 0 )
return m_color_stops . first ( ) . color ;
if ( index > = static_cast < i64 > ( m_gradient_line_colors . size ( ) ) )
return m_color_stops . last ( ) . color ;
return m_gradient_line_colors [ index ] ;
2023-01-10 01:07:06 +00:00
}
Color sample_color ( float loc ) const
{
2023-01-17 19:52:02 +00:00
if ( ! isfinite ( loc ) )
return Color ( ) ;
2023-01-10 01:07:06 +00:00
if ( m_sample_scale ! = 1.0f )
loc * = m_sample_scale ;
auto repeat_wrap_if_required = [ & ] ( i64 loc ) {
2023-08-19 18:12:00 +00:00
if ( m_repeat_mode ! = RepeatMode : : None ) {
auto current_loc = loc + m_start_offset ;
auto gradient_len = static_cast < i64 > ( m_gradient_line_colors . size ( ) ) ;
if ( m_repeat_mode = = RepeatMode : : Repeat ) {
auto color_loc = current_loc % gradient_len ;
return color_loc < 0 ? gradient_len + color_loc : color_loc ;
} else if ( m_repeat_mode = = RepeatMode : : Reflect ) {
auto color_loc = AK : : abs ( current_loc % gradient_len ) ;
auto repeats = current_loc / gradient_len ;
return ( repeats & 1 ) ? gradient_len - color_loc : color_loc ;
}
}
2023-01-10 01:07:06 +00:00
return loc ;
} ;
auto int_loc = static_cast < i64 > ( floor ( loc ) ) ;
auto blend = loc - int_loc ;
auto color = get_color ( repeat_wrap_if_required ( int_loc ) ) ;
2023-05-06 14:28:34 +00:00
// Blend between the two neighboring colors (this fixes some nasty aliasing issues at small angles)
2023-01-10 01:07:06 +00:00
if ( blend > = 0.004f )
2023-01-23 00:35:57 +00:00
color = color_blend ( color , get_color ( repeat_wrap_if_required ( int_loc + 1 ) ) , blend ) ;
2023-01-10 01:07:06 +00:00
return color ;
}
2024-07-05 08:40:29 +00:00
void paint_into_physical_rect ( DeprecatedPainter & painter , IntRect rect , auto location_transform )
2023-01-10 01:07:06 +00:00
{
2024-06-05 06:17:28 +00:00
auto clipped_rect = rect . intersected ( painter . clip_rect ( ) ) ;
2023-01-10 01:07:06 +00:00
auto start_offset = clipped_rect . location ( ) - rect . location ( ) ;
for ( int y = 0 ; y < clipped_rect . height ( ) ; y + + ) {
for ( int x = 0 ; x < clipped_rect . width ( ) ; x + + ) {
auto pixel = sample_color ( location_transform ( x + start_offset . x ( ) , y + start_offset . y ( ) ) ) ;
painter . set_physical_pixel ( clipped_rect . location ( ) . translated ( x , y ) , pixel , m_requires_blending ) ;
}
}
}
2023-08-19 18:12:00 +00:00
bool repeating ( ) const
{
return m_repeat_mode ! = RepeatMode : : None ;
}
enum class RepeatMode {
None ,
Repeat ,
Reflect
} ;
void set_repeat_mode ( RepeatMode repeat_mode )
{
// Note: A gradient can be set to repeating without a repeat length.
// The repeat length is used for CSS gradients but not for SVG gradients.
m_repeat_mode = repeat_mode ;
}
2023-01-10 01:07:06 +00:00
private :
2023-08-19 18:12:00 +00:00
RepeatMode m_repeat_mode { RepeatMode : : None } ;
2023-01-23 00:35:57 +00:00
int m_start_offset { 0 } ;
2023-01-10 01:07:06 +00:00
float m_sample_scale { 1 } ;
2023-02-05 19:02:54 +00:00
ReadonlySpan < ColorStop > m_color_stops { } ;
2024-08-02 09:37:45 +00:00
AlphaType m_alpha_type { AlphaType : : Premultiplied } ;
2023-01-10 01:07:06 +00:00
2023-01-23 00:35:57 +00:00
Vector < Color , 1024 > m_gradient_line_colors ;
2023-01-10 01:07:06 +00:00
bool m_requires_blending = false ;
} ;
2023-01-15 22:10:36 +00:00
template < typename TransformFunction >
struct Gradient {
Gradient ( GradientLine gradient_line , TransformFunction transform_function )
: m_gradient_line ( move ( gradient_line ) )
, m_transform_function ( move ( transform_function ) )
{
}
2023-01-10 01:07:06 +00:00
2024-07-05 08:40:29 +00:00
void paint ( DeprecatedPainter & painter , IntRect rect )
2023-01-15 22:10:36 +00:00
{
m_gradient_line . paint_into_physical_rect ( painter , rect , m_transform_function ) ;
}
2023-05-02 21:43:46 +00:00
template < typename CoordinateType = int >
auto sample_function ( )
2023-01-15 22:10:36 +00:00
{
2023-05-02 21:43:46 +00:00
return [ this ] ( Point < CoordinateType > point ) {
2023-01-15 22:10:36 +00:00
return m_gradient_line . sample_color ( m_transform_function ( point . x ( ) , point . y ( ) ) ) ;
} ;
}
2023-08-19 18:12:00 +00:00
GradientLine & gradient_line ( )
{
return m_gradient_line ;
}
2023-01-15 22:10:36 +00:00
private :
GradientLine m_gradient_line ;
TransformFunction m_transform_function ;
} ;
2024-08-02 09:37:45 +00:00
static auto create_conic_gradient ( ReadonlySpan < ColorStop > color_stops , FloatPoint center_point , float start_angle , Optional < float > repeat_length , AlphaType alpha_type = AlphaType : : Premultiplied )
2023-01-10 01:07:06 +00:00
{
// FIXME: Do we need/want sub-degree accuracy for the gradient line?
2024-08-02 09:37:45 +00:00
GradientLine gradient_line ( 360 , color_stops , repeat_length , alpha_type ) ;
2023-01-10 01:07:06 +00:00
float normalized_start_angle = ( 360.0f - start_angle ) + 90.0f ;
// The flooring can make gradients that want soft edges look worse, so only floor if we have hard edges.
// Which makes sure the hard edge stay hard edges :^)
bool should_floor_angles = false ;
for ( size_t i = 0 ; i < color_stops . size ( ) - 1 ; i + + ) {
if ( color_stops [ i + 1 ] . position - color_stops [ i ] . position < = 0.01f ) {
should_floor_angles = true ;
break ;
}
}
2023-01-15 22:10:36 +00:00
return Gradient {
move ( gradient_line ) ,
[ = ] ( int x , int y ) {
auto point = FloatPoint { x , y } - center_point ;
// FIXME: We could probably get away with some approximation here:
2023-09-09 12:43:39 +00:00
auto loc = fmod ( ( AK : : to_degrees ( AK : : atan2 ( point . y ( ) , point . x ( ) ) ) + 360.0f + normalized_start_angle ) , 360.0f ) ;
2023-01-15 22:10:36 +00:00
return should_floor_angles ? floor ( loc ) : loc ;
}
} ;
}
2023-01-17 19:52:02 +00:00
// The following implements the gradient fill/stoke styles for the HTML canvas: https://html.spec.whatwg.org/multipage/canvas.html#fill-and-stroke-styles
static auto make_sample_non_relative ( IntPoint draw_location , auto sample )
{
return [ = , sample = move ( sample ) ] ( IntPoint point ) { return sample ( point . translated ( draw_location ) ) ; } ;
}
2023-04-22 17:31:00 +00:00
static auto make_linear_gradient_between_two_points ( FloatPoint p0 , FloatPoint p1 , ReadonlySpan < ColorStop > color_stops , Optional < float > repeat_length )
{
auto delta = p1 - p0 ;
auto angle = AK : : atan2 ( delta . y ( ) , delta . x ( ) ) ;
float sin_angle , cos_angle ;
AK : : sincos ( angle , sin_angle , cos_angle ) ;
int gradient_length = ceilf ( p1 . distance_from ( p0 ) ) ;
auto rotated_start_point_x = p0 . x ( ) * cos_angle - p0 . y ( ) * - sin_angle ;
return Gradient {
2024-08-02 09:37:45 +00:00
GradientLine ( gradient_length , color_stops , repeat_length , AlphaType : : Unpremultiplied ) ,
2023-04-22 17:31:00 +00:00
[ = ] ( int x , int y ) {
return ( x * cos_angle - y * - sin_angle ) - rotated_start_point_x ;
}
} ;
}
2023-01-17 19:52:02 +00:00
void CanvasLinearGradientPaintStyle : : paint ( IntRect physical_bounding_box , PaintFunction paint ) const
{
// If x0 = x1 and y0 = y1, then the linear gradient must paint nothing.
if ( m_p0 = = m_p1 )
return ;
if ( color_stops ( ) . is_empty ( ) )
return ;
if ( color_stops ( ) . size ( ) < 2 )
return paint ( [ this ] ( IntPoint ) { return color_stops ( ) . first ( ) . color ; } ) ;
2023-04-22 17:31:00 +00:00
auto linear_gradient = make_linear_gradient_between_two_points ( m_p0 , m_p1 , color_stops ( ) , repeat_length ( ) ) ;
paint ( make_sample_non_relative ( physical_bounding_box . location ( ) , linear_gradient . sample_function ( ) ) ) ;
}
2023-01-17 19:52:02 +00:00
2023-08-19 18:12:00 +00:00
static GradientLine : : RepeatMode svg_spread_method_to_repeat_mode ( SVGGradientPaintStyle : : SpreadMethod spread_method )
{
switch ( spread_method ) {
case SVGGradientPaintStyle : : SpreadMethod : : Pad :
return GradientLine : : RepeatMode : : None ;
case SVGGradientPaintStyle : : SpreadMethod : : Reflect :
return GradientLine : : RepeatMode : : Reflect ;
case SVGGradientPaintStyle : : SpreadMethod : : Repeat :
return GradientLine : : RepeatMode : : Repeat ;
default :
VERIFY_NOT_REACHED ( ) ;
}
}
2023-05-02 21:43:46 +00:00
void SVGGradientPaintStyle : : set_gradient_transform ( AffineTransform transform )
{
// Note: The scaling is removed so enough points on the gradient line are generated.
// Otherwise, if you scale a tiny path the gradient looks pixelated.
m_scale = 1.0f ;
if ( auto inverse = transform . inverse ( ) ; inverse . has_value ( ) ) {
auto transform_scale = transform . scale ( ) ;
m_scale = max ( transform_scale . x ( ) , transform_scale . y ( ) ) ;
m_inverse_transform = AffineTransform { } . scale ( m_scale , m_scale ) . multiply ( * inverse ) ;
} else {
m_inverse_transform = OptionalNone { } ;
}
}
2023-04-22 17:31:00 +00:00
void SVGLinearGradientPaintStyle : : paint ( IntRect physical_bounding_box , PaintFunction paint ) const
{
if ( color_stops ( ) . is_empty ( ) )
return ;
// If ‘ x1’ = ‘ x2’ and ‘ y1’ = ‘ y2’ , then the area to be painted will be painted as
// a single color using the color and opacity of the last gradient stop.
if ( m_p0 = = m_p1 )
return paint ( [ this ] ( IntPoint ) { return color_stops ( ) . last ( ) . color ; } ) ;
if ( color_stops ( ) . size ( ) < 2 )
return paint ( [ this ] ( IntPoint ) { return color_stops ( ) . first ( ) . color ; } ) ;
2023-05-02 21:43:46 +00:00
float scale = gradient_transform_scale ( ) ;
auto linear_gradient = make_linear_gradient_between_two_points (
m_p0 . scaled ( scale , scale ) , m_p1 . scaled ( scale , scale ) ,
color_stops ( ) , repeat_length ( ) ) ;
2023-08-19 18:12:00 +00:00
linear_gradient . gradient_line ( ) . set_repeat_mode (
svg_spread_method_to_repeat_mode ( spread_method ( ) ) ) ;
2023-04-22 17:31:00 +00:00
2023-05-02 21:43:46 +00:00
paint ( [ & , sampler = linear_gradient . sample_function < float > ( ) ] ( IntPoint target_point ) {
auto point = target_point . translated ( physical_bounding_box . location ( ) ) . to_type < float > ( ) ;
if ( auto inverse_transform = scale_adjusted_inverse_gradient_transform ( ) ; inverse_transform . has_value ( ) )
point = inverse_transform - > map ( point ) ;
2023-04-22 17:31:00 +00:00
return sampler ( point ) ;
} ) ;
2023-01-17 19:52:02 +00:00
}
void CanvasConicGradientPaintStyle : : paint ( IntRect physical_bounding_box , PaintFunction paint ) const
{
if ( color_stops ( ) . is_empty ( ) )
return ;
if ( color_stops ( ) . size ( ) < 2 )
return paint ( [ this ] ( IntPoint ) { return color_stops ( ) . first ( ) . color ; } ) ;
// Follows the same rendering rule as CSS 'conic-gradient' and it is equivalent to CSS
// 'conic-gradient(from adjustedStartAnglerad at xpx ypx, angularColorStopList)'.
// Here:
// adjustedStartAngle is given by startAngle + π/2;
2024-08-02 09:37:45 +00:00
auto conic_gradient = create_conic_gradient ( color_stops ( ) , m_center , m_start_angle + 90.0f , repeat_length ( ) , AlphaType : : Unpremultiplied ) ;
2023-01-17 19:52:02 +00:00
paint ( make_sample_non_relative ( physical_bounding_box . location ( ) , conic_gradient . sample_function ( ) ) ) ;
}
2023-05-02 21:43:46 +00:00
static auto create_radial_gradient_between_two_circles ( Gfx : : FloatPoint start_center , float start_radius , Gfx : : FloatPoint end_center , float end_radius , ReadonlySpan < ColorStop > color_stops , Optional < float > repeat_length )
2023-01-17 19:52:02 +00:00
{
bool reverse_gradient = end_radius < start_radius ;
if ( reverse_gradient ) {
swap ( end_radius , start_radius ) ;
swap ( end_center , start_center ) ;
}
2023-08-22 21:29:32 +00:00
// FIXME: Handle the start_radius == end_radius special case separately.
// This hack is not quite correct.
if ( end_radius - start_radius < 1 )
end_radius + = 1 ;
2023-01-17 19:52:02 +00:00
// Spec steps: Useless for writing an actual implementation (give it a go :P):
//
// 2. Let x(ω) = (x1-x0)ω + x0
// Let y(ω) = (y1-y0)ω + y0
// Let r(ω) = (r1-r0)ω + r0
// Let the color at ω be the color at that position on the gradient
// (with the colors coming from the interpolation and extrapolation described above).
//
// 3. For all values of ω where r(ω) > 0, starting with the value of ω nearest to positive infinity and
// ending with the value of ω nearest to negative infinity, draw the circumference of the circle with
// radius r(ω) at position (x(ω), y(ω)), with the color at ω, but only painting on the parts of the
// bitmap that have not yet been painted on by earlier circles in this step for this rendering of the gradient.
auto center_dist = end_center . distance_from ( start_center ) ;
bool inner_contained = ( ( center_dist + start_radius ) < end_radius ) ;
auto start_point = start_center ;
2023-08-21 22:03:42 +00:00
if ( start_radius ! = 0 ) {
// Set the start point to the focal point.
auto f = end_radius / ( end_radius - start_radius ) ;
auto one_minus_f = 1 - f ;
start_point = start_center . scaled ( f ) + end_center . scaled ( one_minus_f ) ;
2023-01-17 19:52:02 +00:00
}
// This is just an approximate upperbound (the gradient line class will shorten this if necessary).
2023-01-23 00:35:57 +00:00
int gradient_length = AK : : ceil ( center_dist + end_radius + start_radius ) ;
2024-08-02 09:37:45 +00:00
GradientLine gradient_line ( gradient_length , color_stops , repeat_length , AlphaType : : Unpremultiplied ) ;
2023-01-17 19:52:02 +00:00
// If you can simplify this please do, this is "best guess" implementation due to lack of specification.
// It was implemented to visually match chrome/firefox in all cases:
// - Start circle inside end circle
// - Start circle outside end circle
// - Start circle radius == end circle radius
// - Start circle larger than end circle (inside end circle)
// - Start circle larger than end circle (outside end circle)
2023-04-12 17:32:21 +00:00
// - Start circle or end circle radius == 0
2023-01-17 19:52:02 +00:00
2023-08-21 22:03:42 +00:00
auto circle_distance_finder = [ = ] ( auto radius , auto center ) {
auto radius2 = radius * radius ;
auto delta = center - start_point ;
2023-08-22 21:29:32 +00:00
auto delta_xy = delta . x ( ) * delta . y ( ) ;
auto dx2_factor = radius2 - delta . y ( ) * delta . y ( ) ;
auto dy2_factor = radius2 - delta . x ( ) * delta . x ( ) ;
return [ = ] ( bool positive_root , auto vec ) {
2023-08-21 22:03:42 +00:00
// This works out the distance to the nearest point on the circle
// in the direction of the "vec" vector.
auto dx2 = vec . x ( ) * vec . x ( ) ;
auto dy2 = vec . y ( ) * vec . y ( ) ;
auto root = sqrtf ( dx2 * dx2_factor + dy2 * dy2_factor
2023-08-22 21:29:32 +00:00
+ 2 * vec . x ( ) * vec . y ( ) * delta_xy ) ;
2023-08-21 22:03:42 +00:00
auto dot = vec . x ( ) * delta . x ( ) + vec . y ( ) * delta . y ( ) ;
2023-08-22 21:29:32 +00:00
return ( ( positive_root ? root : - root ) + dot ) / ( dx2 + dy2 ) ;
2023-08-21 22:03:42 +00:00
} ;
} ;
auto end_circle_dist = circle_distance_finder ( end_radius , end_center ) ;
2023-08-22 21:29:32 +00:00
auto start_circle_dist = [ = , dist = circle_distance_finder ( start_radius , start_center ) ] ( bool positive_root , auto vec ) {
2023-08-21 22:03:42 +00:00
if ( start_center = = start_point )
return start_radius ;
2023-08-22 21:29:32 +00:00
return dist ( positive_root , vec ) ;
2023-08-21 22:03:42 +00:00
} ;
2023-08-19 18:24:00 +00:00
2023-05-02 21:43:46 +00:00
return Gradient {
2023-01-17 19:52:02 +00:00
move ( gradient_line ) ,
2023-05-02 21:43:46 +00:00
[ = ] ( float x , float y ) {
2023-08-22 21:29:32 +00:00
auto loc = [ & ] {
2023-01-17 19:52:02 +00:00
FloatPoint point { x , y } ;
2023-08-22 21:29:43 +00:00
// Add a little to avoid division by zero at the focal point.
if ( point = = start_point )
point + = FloatPoint { 0.001f , 0.001f } ;
2023-08-21 22:03:42 +00:00
// The "vec" (unit) vector points from the focal point to the current point.
2023-08-22 21:29:32 +00:00
auto dist = point . distance_from ( start_point ) ;
2023-01-17 19:52:02 +00:00
auto vec = ( point - start_point ) / dist ;
2023-08-22 21:29:32 +00:00
bool use_positive_root = inner_contained | | reverse_gradient ;
auto dist_end = end_circle_dist ( use_positive_root , vec ) ;
auto dist_start = start_circle_dist ( use_positive_root , vec ) ;
2023-01-17 19:52:02 +00:00
// FIXME: Returning nan is a hack for "Don't paint me!"
2023-08-21 22:03:42 +00:00
if ( dist_end < 0 )
2023-01-17 19:52:02 +00:00
return AK : : NaN < float > ;
2023-08-21 22:03:42 +00:00
if ( dist_end - dist_start < 0 )
2023-01-17 19:52:02 +00:00
return float ( gradient_length ) ;
2023-08-21 22:03:42 +00:00
return ( dist - dist_start ) / ( dist_end - dist_start ) ;
2023-08-22 21:29:32 +00:00
} ( ) ;
2023-01-17 19:52:02 +00:00
if ( reverse_gradient )
loc = 1.0f - loc ;
return loc * gradient_length ;
}
} ;
2023-05-02 21:43:46 +00:00
}
2023-01-17 19:52:02 +00:00
2023-05-02 21:43:46 +00:00
void CanvasRadialGradientPaintStyle : : paint ( IntRect physical_bounding_box , PaintFunction paint ) const
{
// 1. If x0 = x1 and y0 = y1 and r0 = r1, then the radial gradient must paint nothing. Return.
if ( m_start_center = = m_end_center & & m_start_radius = = m_end_radius )
return ;
if ( color_stops ( ) . is_empty ( ) )
return ;
if ( color_stops ( ) . size ( ) < 2 )
return paint ( [ this ] ( IntPoint ) { return color_stops ( ) . first ( ) . color ; } ) ;
if ( m_end_radius = = 0 & & m_start_radius = = 0 )
return ;
auto radial_gradient = create_radial_gradient_between_two_circles ( m_start_center , m_start_radius , m_end_center , m_end_radius , color_stops ( ) , repeat_length ( ) ) ;
2023-01-17 19:52:02 +00:00
paint ( make_sample_non_relative ( physical_bounding_box . location ( ) , radial_gradient . sample_function ( ) ) ) ;
}
2023-05-02 21:43:46 +00:00
void SVGRadialGradientPaintStyle : : paint ( IntRect physical_bounding_box , PaintFunction paint ) const
{
// FIXME: Ensure this handles all the edge cases of SVG gradients.
if ( color_stops ( ) . is_empty ( ) )
return ;
if ( color_stops ( ) . size ( ) < 2 | | ( m_end_radius = = 0 & & m_start_radius = = 0 ) )
return paint ( [ this ] ( IntPoint ) { return color_stops ( ) . last ( ) . color ; } ) ;
float scale = gradient_transform_scale ( ) ;
auto radial_gradient = create_radial_gradient_between_two_circles (
m_start_center . scaled ( scale , scale ) , m_start_radius * scale , m_end_center . scaled ( scale , scale ) , m_end_radius * scale ,
color_stops ( ) , repeat_length ( ) ) ;
2023-08-19 18:12:00 +00:00
radial_gradient . gradient_line ( ) . set_repeat_mode (
svg_spread_method_to_repeat_mode ( spread_method ( ) ) ) ;
2023-05-02 21:43:46 +00:00
paint ( [ & , sampler = radial_gradient . sample_function < float > ( ) ] ( IntPoint target_point ) {
auto point = target_point . translated ( physical_bounding_box . location ( ) ) . to_type < float > ( ) ;
if ( auto inverse_transform = scale_adjusted_inverse_gradient_transform ( ) ; inverse_transform . has_value ( ) )
point = inverse_transform - > map ( point ) ;
return sampler ( point ) ;
} ) ;
}
2023-01-10 01:07:06 +00:00
}