diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index bef7068f44df93101b0d698114f25f3f58af173e..37ec17908e63ea0ec9555ac12997d47de64fe39f 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -2296,25 +2296,44 @@ class Gradient extends Shader { /// If `matrix4` is provided, the gradient fill will be transformed by the /// specified 4x4 matrix relative to the local coordinate system. `matrix4` must /// be a column-major matrix packed into a list of 16 values. + /// + /// If `focal` is provided and not equal to `center` or `focalRadius` is + /// provided and not equal to 0.0, the generated shader will be a two point + /// conical radial gradient, with `focal` being the center of the focal + /// circle and `focalRadius` being the radius of that circle. If `focal` is + /// provided and not equal to `center`, at least one of the two offsets must + /// not be equal to [Offset.zero]. Gradient.radial( Offset center, double radius, List colors, [ List colorStops, TileMode tileMode = TileMode.clamp, - Float64List matrix4 + Float64List matrix4, + Offset focal, + double focalRadius ]) : assert(_offsetIsValid(center)), assert(colors != null), assert(tileMode != null), assert(matrix4 == null || _matrix4IsValid(matrix4)), super._() { + focal ??= center; + focalRadius ??= 0.0; _validateColorStops(colors, colorStops); final Int32List colorsBuffer = _encodeColorList(colors); final Float32List colorStopsBuffer = colorStops == null ? null : new Float32List.fromList(colorStops); - _constructor(); - _initRadial(center.dx, center.dy, radius, colorsBuffer, colorStopsBuffer, tileMode.index, matrix4); + + if (center == focal && focalRadius != 0.0) { + _constructor(); + _initRadial(center.dx, center.dy, radius, colorsBuffer, colorStopsBuffer, tileMode.index, matrix4); + } else { + assert(center != Offset.zero || focal != Offset.zero); // will result in nullptr in Skia side + _constructor(); + _initConical(focal.dx, focal.dy, focalRadius, center.dx, center.dy, radius, colorsBuffer, colorStopsBuffer, tileMode.index, matrix4); + } } void _initRadial(double centerX, double centerY, double radius, Int32List colors, Float32List colorStops, int tileMode, Float64List matrix4) native 'Gradient_initRadial'; + void _initConical(double startX, double startY, double startRadius, double endX, double endY, double endRadius, Int32List colors, Float32List colorStops, int tileMode, Float64List matrix4) native 'Gradient_initTwoPointConical'; /// Creates a sweep gradient centered at `center` that starts at `startAngle` /// and ends at `endAngle`. diff --git a/lib/ui/painting/gradient.cc b/lib/ui/painting/gradient.cc index 9cbf5a19c5275813cd61aa3d77ac0e738ed85b7b..71b2669100fa1f86a562fc4d8ceb650ae8f206c8 100644 --- a/lib/ui/painting/gradient.cc +++ b/lib/ui/painting/gradient.cc @@ -25,7 +25,8 @@ IMPLEMENT_WRAPPERTYPEINFO(ui, Gradient); #define FOR_EACH_BINDING(V) \ V(Gradient, initLinear) \ V(Gradient, initRadial) \ - V(Gradient, initSweep) + V(Gradient, initSweep) \ + V(Gradient, initTwoPointConical) FOR_EACH_BINDING(DART_NATIVE_CALLBACK) @@ -109,6 +110,35 @@ void CanvasGradient::initSweep(double center_x, has_matrix ? &sk_matrix : nullptr))); } +void CanvasGradient::initTwoPointConical(double start_x, + double start_y, + double start_radius, + double end_x, + double end_y, + double end_radius, + const tonic::Int32List& colors, + const tonic::Float32List& color_stops, + SkShader::TileMode tile_mode, + const tonic::Float64List& matrix4) { + FXL_DCHECK(colors.num_elements() == color_stops.num_elements() || + color_stops.data() == nullptr); + + static_assert(sizeof(SkColor) == sizeof(int32_t), + "SkColor doesn't use int32_t."); + + SkMatrix sk_matrix; + bool has_matrix = matrix4.data() != nullptr; + if (has_matrix) { + sk_matrix = ToSkMatrix(matrix4); + } + + set_shader(UIDartState::CreateGPUObject(SkGradientShader::MakeTwoPointConical( + SkPoint::Make(start_x, start_y), start_radius, + SkPoint::Make(end_x, end_y), end_radius, + reinterpret_cast(colors.data()), color_stops.data(), + colors.num_elements(), tile_mode, 0, has_matrix ? &sk_matrix : nullptr))); +} + CanvasGradient::CanvasGradient() = default; CanvasGradient::~CanvasGradient() = default; diff --git a/lib/ui/painting/gradient.h b/lib/ui/painting/gradient.h index 178c17fe9b41c5aa2d080e6d5a9100cea3511370..4d8e8ec09191bef27e1c853f13f630897471a383 100644 --- a/lib/ui/painting/gradient.h +++ b/lib/ui/painting/gradient.h @@ -52,6 +52,17 @@ class CanvasGradient : public Shader { double end_angle, const tonic::Float64List& matrix4); + void initTwoPointConical(double start_x, + double start_y, + double start_radius, + double end_x, + double end_y, + double end_radius, + const tonic::Int32List& colors, + const tonic::Float32List& color_stops, + SkShader::TileMode tile_mode, + const tonic::Float64List& matrix4); + static void RegisterNatives(tonic::DartLibraryNatives* natives); private: