Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 7418367

Browse files
authored
Framework wide color linear gradients (#54748)
This implements wide gamut color support for gradients in the engine. issue: flutter/flutter#127855 integration test: flutter/flutter#153976 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent 9838d3c commit 7418367

File tree

4 files changed

+80
-45
lines changed

4 files changed

+80
-45
lines changed

impeller/display_list/skia_conversions.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,8 @@ std::optional<impeller::PixelFormat> ToPixelFormat(SkColorType type) {
218218
void ConvertStops(const flutter::DlGradientColorSourceBase* gradient,
219219
std::vector<Color>& colors,
220220
std::vector<float>& stops) {
221-
FML_DCHECK(gradient->stop_count() >= 2);
221+
FML_DCHECK(gradient->stop_count() >= 2)
222+
<< "stop_count:" << gradient->stop_count();
222223

223224
auto* dl_colors = gradient->colors();
224225
auto* dl_stops = gradient->stops();

lib/ui/painting.dart

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4452,10 +4452,25 @@ enum TileMode {
44524452
decal,
44534453
}
44544454

4455+
Float32List _encodeWideColorList(List<Color> colors) {
4456+
final int colorCount = colors.length;
4457+
final Float32List result = Float32List(colorCount * 4);
4458+
for (int i = 0; i < colorCount; i++) {
4459+
final Color colorXr =
4460+
colors[i].withValues(colorSpace: ColorSpace.extendedSRGB);
4461+
result[i*4+0] = colorXr.a;
4462+
result[i*4+1] = colorXr.r;
4463+
result[i*4+2] = colorXr.g;
4464+
result[i*4+3] = colorXr.b;
4465+
}
4466+
return result;
4467+
}
4468+
4469+
44554470
Int32List _encodeColorList(List<Color> colors) {
44564471
final int colorCount = colors.length;
44574472
final Int32List result = Int32List(colorCount);
4458-
for (int i = 0; i < colorCount; ++i) {
4473+
for (int i = 0; i < colorCount; i++) {
44594474
result[i] = colors[i].value;
44604475
}
44614476
return result;
@@ -4464,7 +4479,7 @@ Int32List _encodeColorList(List<Color> colors) {
44644479
Float32List _encodePointList(List<Offset> points) {
44654480
final int pointCount = points.length;
44664481
final Float32List result = Float32List(pointCount * 2);
4467-
for (int i = 0; i < pointCount; ++i) {
4482+
for (int i = 0; i < pointCount; i++) {
44684483
final int xIndex = i * 2;
44694484
final int yIndex = xIndex + 1;
44704485
final Offset point = points[i];
@@ -4535,7 +4550,7 @@ base class Gradient extends Shader {
45354550
super._() {
45364551
_validateColorStops(colors, colorStops);
45374552
final Float32List endPointsBuffer = _encodeTwoPoints(from, to);
4538-
final Int32List colorsBuffer = _encodeColorList(colors);
4553+
final Float32List colorsBuffer = _encodeWideColorList(colors);
45394554
final Float32List? colorStopsBuffer = colorStops == null ? null : Float32List.fromList(colorStops);
45404555
_constructor();
45414556
_initLinear(endPointsBuffer, colorsBuffer, colorStopsBuffer, tileMode.index, matrix4);
@@ -4588,8 +4603,8 @@ base class Gradient extends Shader {
45884603
assert(matrix4 == null || _matrix4IsValid(matrix4)),
45894604
super._() {
45904605
_validateColorStops(colors, colorStops);
4591-
final Int32List colorsBuffer = _encodeColorList(colors);
45924606
final Float32List? colorStopsBuffer = colorStops == null ? null : Float32List.fromList(colorStops);
4607+
final Float32List colorsBuffer = _encodeWideColorList(colors);
45934608

45944609
// If focal is null or focal radius is null, this should be treated as a regular radial gradient
45954610
// If focal == center and the focal radius is 0.0, it's still a regular radial gradient
@@ -4647,7 +4662,7 @@ base class Gradient extends Shader {
46474662
assert(matrix4 == null || _matrix4IsValid(matrix4)),
46484663
super._() {
46494664
_validateColorStops(colors, colorStops);
4650-
final Int32List colorsBuffer = _encodeColorList(colors);
4665+
final Float32List colorsBuffer = _encodeWideColorList(colors);
46514666
final Float32List? colorStopsBuffer = colorStops == null ? null : Float32List.fromList(colorStops);
46524667
_constructor();
46534668
_initSweep(center.dx, center.dy, colorsBuffer, colorStopsBuffer, tileMode.index, startAngle, endAngle, matrix4);
@@ -4657,14 +4672,14 @@ base class Gradient extends Shader {
46574672
external void _constructor();
46584673

46594674
@Native<Void Function(Pointer<Void>, Handle, Handle, Handle, Int32, Handle)>(symbol: 'Gradient::initLinear')
4660-
external void _initLinear(Float32List endPoints, Int32List colors, Float32List? colorStops, int tileMode, Float64List? matrix4);
4675+
external void _initLinear(Float32List endPoints, Float32List colors, Float32List? colorStops, int tileMode, Float64List? matrix4);
46614676

46624677
@Native<Void Function(Pointer<Void>, Double, Double, Double, Handle, Handle, Int32, Handle)>(symbol: 'Gradient::initRadial')
46634678
external void _initRadial(
46644679
double centerX,
46654680
double centerY,
46664681
double radius,
4667-
Int32List colors,
4682+
Float32List colors,
46684683
Float32List? colorStops,
46694684
int tileMode,
46704685
Float64List? matrix4);
@@ -4677,7 +4692,7 @@ base class Gradient extends Shader {
46774692
double endX,
46784693
double endY,
46794694
double endRadius,
4680-
Int32List colors,
4695+
Float32List colors,
46814696
Float32List? colorStops,
46824697
int tileMode,
46834698
Float64List? matrix4);
@@ -4686,7 +4701,7 @@ base class Gradient extends Shader {
46864701
external void _initSweep(
46874702
double centerX,
46884703
double centerY,
4689-
Int32List colors,
4704+
Float32List colors,
46904705
Float32List? colorStops,
46914706
int tileMode,
46924707
double startAngle,
@@ -6503,7 +6518,7 @@ base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas {
65036518
final Float32List rstTransformBuffer = Float32List(rectCount * 4);
65046519
final Float32List rectBuffer = Float32List(rectCount * 4);
65056520

6506-
for (int i = 0; i < rectCount; ++i) {
6521+
for (int i = 0; i < rectCount; i++) {
65076522
final int index0 = i * 4;
65086523
final int index1 = index0 + 1;
65096524
final int index2 = index0 + 2;

lib/ui/painting/gradient.cc

Lines changed: 49 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@ void CanvasGradient::Create(Dart_Handle wrapper) {
2424
}
2525

2626
void CanvasGradient::initLinear(const tonic::Float32List& end_points,
27-
const tonic::Int32List& colors,
27+
const tonic::Float32List& colors,
2828
const tonic::Float32List& color_stops,
2929
DlTileMode tile_mode,
3030
const tonic::Float64List& matrix4) {
3131
FML_DCHECK(end_points.num_elements() == 4);
32-
FML_DCHECK(colors.num_elements() == color_stops.num_elements() ||
32+
FML_DCHECK(colors.num_elements() == (color_stops.num_elements() * 4) ||
3333
color_stops.data() == nullptr);
34+
int num_colors = colors.num_elements() / 4;
3435

3536
static_assert(sizeof(SkPoint) == sizeof(float) * 2,
3637
"SkPoint doesn't use floats.");
@@ -46,28 +47,32 @@ void CanvasGradient::initLinear(const tonic::Float32List& end_points,
4647
SkPoint p0 = SkPoint::Make(end_points[0], end_points[1]);
4748
SkPoint p1 = SkPoint::Make(end_points[2], end_points[3]);
4849
std::vector<DlColor> dl_colors;
49-
dl_colors.reserve(colors.num_elements());
50-
for (int i = 0; i < colors.num_elements(); ++i) {
51-
/// TODO(gaaclarke): Make this preserve wide gamut colors.
52-
dl_colors.emplace_back(DlColor(colors[i]));
50+
dl_colors.reserve(num_colors);
51+
for (int i = 0; i < colors.num_elements(); i += 4) {
52+
DlScalar a = colors[i + 0];
53+
DlScalar r = colors[i + 1];
54+
DlScalar g = colors[i + 2];
55+
DlScalar b = colors[i + 3];
56+
dl_colors.emplace_back(DlColor(a, r, g, b, DlColorSpace::kExtendedSRGB));
5357
}
5458

55-
dl_shader_ = DlColorSource::MakeLinear(
56-
p0, p1, colors.num_elements(), dl_colors.data(), color_stops.data(),
57-
tile_mode, has_matrix ? &sk_matrix : nullptr);
59+
dl_shader_ = DlColorSource::MakeLinear(p0, p1, num_colors, dl_colors.data(),
60+
color_stops.data(), tile_mode,
61+
has_matrix ? &sk_matrix : nullptr);
5862
// Just a sanity check, all gradient shaders should be thread-safe
5963
FML_DCHECK(dl_shader_->isUIThreadSafe());
6064
}
6165

6266
void CanvasGradient::initRadial(double center_x,
6367
double center_y,
6468
double radius,
65-
const tonic::Int32List& colors,
69+
const tonic::Float32List& colors,
6670
const tonic::Float32List& color_stops,
6771
DlTileMode tile_mode,
6872
const tonic::Float64List& matrix4) {
69-
FML_DCHECK(colors.num_elements() == color_stops.num_elements() ||
73+
FML_DCHECK(colors.num_elements() == (color_stops.num_elements() * 4) ||
7074
color_stops.data() == nullptr);
75+
int num_colors = colors.num_elements() / 4;
7176

7277
static_assert(sizeof(SkColor) == sizeof(int32_t),
7378
"SkColor doesn't use int32_t.");
@@ -79,29 +84,34 @@ void CanvasGradient::initRadial(double center_x,
7984
}
8085

8186
std::vector<DlColor> dl_colors;
82-
dl_colors.reserve(colors.num_elements());
83-
for (int i = 0; i < colors.num_elements(); ++i) {
84-
dl_colors.emplace_back(DlColor(colors[i]));
87+
dl_colors.reserve(num_colors);
88+
for (int i = 0; i < colors.num_elements(); i += 4) {
89+
DlScalar a = colors[i + 0];
90+
DlScalar r = colors[i + 1];
91+
DlScalar g = colors[i + 2];
92+
DlScalar b = colors[i + 3];
93+
dl_colors.emplace_back(DlColor(a, r, g, b, DlColorSpace::kExtendedSRGB));
8594
}
8695

8796
dl_shader_ = DlColorSource::MakeRadial(
8897
SkPoint::Make(SafeNarrow(center_x), SafeNarrow(center_y)),
89-
SafeNarrow(radius), colors.num_elements(), dl_colors.data(),
90-
color_stops.data(), tile_mode, has_matrix ? &sk_matrix : nullptr);
98+
SafeNarrow(radius), num_colors, dl_colors.data(), color_stops.data(),
99+
tile_mode, has_matrix ? &sk_matrix : nullptr);
91100
// Just a sanity check, all gradient shaders should be thread-safe
92101
FML_DCHECK(dl_shader_->isUIThreadSafe());
93102
}
94103

95104
void CanvasGradient::initSweep(double center_x,
96105
double center_y,
97-
const tonic::Int32List& colors,
106+
const tonic::Float32List& colors,
98107
const tonic::Float32List& color_stops,
99108
DlTileMode tile_mode,
100109
double start_angle,
101110
double end_angle,
102111
const tonic::Float64List& matrix4) {
103-
FML_DCHECK(colors.num_elements() == color_stops.num_elements() ||
112+
FML_DCHECK(colors.num_elements() == (color_stops.num_elements() * 4) ||
104113
color_stops.data() == nullptr);
114+
int num_colors = colors.num_elements() / 4;
105115

106116
static_assert(sizeof(SkColor) == sizeof(int32_t),
107117
"SkColor doesn't use int32_t.");
@@ -113,16 +123,20 @@ void CanvasGradient::initSweep(double center_x,
113123
}
114124

115125
std::vector<DlColor> dl_colors;
116-
dl_colors.reserve(colors.num_elements());
117-
for (int i = 0; i < colors.num_elements(); ++i) {
118-
dl_colors.emplace_back(DlColor(colors[i]));
126+
dl_colors.reserve(num_colors);
127+
for (int i = 0; i < colors.num_elements(); i += 4) {
128+
DlScalar a = colors[i + 0];
129+
DlScalar r = colors[i + 1];
130+
DlScalar g = colors[i + 2];
131+
DlScalar b = colors[i + 3];
132+
dl_colors.emplace_back(DlColor(a, r, g, b, DlColorSpace::kExtendedSRGB));
119133
}
120134

121135
dl_shader_ = DlColorSource::MakeSweep(
122136
SkPoint::Make(SafeNarrow(center_x), SafeNarrow(center_y)),
123137
SafeNarrow(start_angle) * 180.0f / static_cast<float>(M_PI),
124-
SafeNarrow(end_angle) * 180.0f / static_cast<float>(M_PI),
125-
colors.num_elements(), dl_colors.data(), color_stops.data(), tile_mode,
138+
SafeNarrow(end_angle) * 180.0f / static_cast<float>(M_PI), num_colors,
139+
dl_colors.data(), color_stops.data(), tile_mode,
126140
has_matrix ? &sk_matrix : nullptr);
127141
// Just a sanity check, all gradient shaders should be thread-safe
128142
FML_DCHECK(dl_shader_->isUIThreadSafe());
@@ -134,12 +148,13 @@ void CanvasGradient::initTwoPointConical(double start_x,
134148
double end_x,
135149
double end_y,
136150
double end_radius,
137-
const tonic::Int32List& colors,
151+
const tonic::Float32List& colors,
138152
const tonic::Float32List& color_stops,
139153
DlTileMode tile_mode,
140154
const tonic::Float64List& matrix4) {
141-
FML_DCHECK(colors.num_elements() == color_stops.num_elements() ||
155+
FML_DCHECK(colors.num_elements() == (color_stops.num_elements() * 4) ||
142156
color_stops.data() == nullptr);
157+
int num_colors = colors.num_elements() / 4;
143158

144159
static_assert(sizeof(SkColor) == sizeof(int32_t),
145160
"SkColor doesn't use int32_t.");
@@ -151,17 +166,21 @@ void CanvasGradient::initTwoPointConical(double start_x,
151166
}
152167

153168
std::vector<DlColor> dl_colors;
154-
dl_colors.reserve(colors.num_elements());
155-
for (int i = 0; i < colors.num_elements(); ++i) {
156-
dl_colors.emplace_back(DlColor(colors[i]));
169+
dl_colors.reserve(num_colors);
170+
for (int i = 0; i < colors.num_elements(); i += 4) {
171+
DlScalar a = colors[i + 0];
172+
DlScalar r = colors[i + 1];
173+
DlScalar g = colors[i + 2];
174+
DlScalar b = colors[i + 3];
175+
dl_colors.emplace_back(DlColor(a, r, g, b, DlColorSpace::kExtendedSRGB));
157176
}
158177

159178
dl_shader_ = DlColorSource::MakeConical(
160179
SkPoint::Make(SafeNarrow(start_x), SafeNarrow(start_y)),
161180
SafeNarrow(start_radius),
162181
SkPoint::Make(SafeNarrow(end_x), SafeNarrow(end_y)),
163-
SafeNarrow(end_radius), colors.num_elements(), dl_colors.data(),
164-
color_stops.data(), tile_mode, has_matrix ? &sk_matrix : nullptr);
182+
SafeNarrow(end_radius), num_colors, dl_colors.data(), color_stops.data(),
183+
tile_mode, has_matrix ? &sk_matrix : nullptr);
165184
// Just a sanity check, all gradient shaders should be thread-safe
166185
FML_DCHECK(dl_shader_->isUIThreadSafe());
167186
}

lib/ui/painting/gradient.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,22 @@ class CanvasGradient : public Shader {
2121
static void Create(Dart_Handle wrapper);
2222

2323
void initLinear(const tonic::Float32List& end_points,
24-
const tonic::Int32List& colors,
24+
const tonic::Float32List& colors,
2525
const tonic::Float32List& color_stops,
2626
DlTileMode tile_mode,
2727
const tonic::Float64List& matrix4);
2828

2929
void initRadial(double center_x,
3030
double center_y,
3131
double radius,
32-
const tonic::Int32List& colors,
32+
const tonic::Float32List& colors,
3333
const tonic::Float32List& color_stops,
3434
DlTileMode tile_mode,
3535
const tonic::Float64List& matrix4);
3636

3737
void initSweep(double center_x,
3838
double center_y,
39-
const tonic::Int32List& colors,
39+
const tonic::Float32List& colors,
4040
const tonic::Float32List& color_stops,
4141
DlTileMode tile_mode,
4242
double start_angle,
@@ -49,7 +49,7 @@ class CanvasGradient : public Shader {
4949
double end_x,
5050
double end_y,
5151
double end_radius,
52-
const tonic::Int32List& colors,
52+
const tonic::Float32List& colors,
5353
const tonic::Float32List& color_stops,
5454
DlTileMode tile_mode,
5555
const tonic::Float64List& matrix4);

0 commit comments

Comments
 (0)