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

Commit f1c6949

Browse files
authored
[Impeller] Add missing advanced blends in Color::BlendColor; absorb any DrawPaint blend mode (#41105)
Resolves flutter/flutter#124623.
1 parent aa74ecc commit f1c6949

File tree

4 files changed

+169
-24
lines changed

4 files changed

+169
-24
lines changed

impeller/aiks/canvas.cc

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,7 @@ void Canvas::DrawPath(const Path& path, const Paint& paint) {
168168

169169
void Canvas::DrawPaint(const Paint& paint) {
170170
if (xformation_stack_.size() == 1 && // If we're recording the root pass,
171-
GetCurrentPass().GetElementCount() == 0 && // and this is the first item,
172-
// TODO(bdero): Fix the advanced blends in Color::BlendColor.
173-
// https://github.com/flutter/flutter/issues/124623
174-
paint.blend_mode <= Entity::kLastPipelineBlendMode //
171+
GetCurrentPass().GetElementCount() == 0 // and this is the first item,
175172
) {
176173
// Then we can absorb this drawPaint as the clear color of the pass.
177174
auto color = Color::BlendColor(

impeller/geometry/color.cc

Lines changed: 106 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <cmath>
99
#include <sstream>
1010

11+
#include "impeller/geometry/constants.h"
1112
#include "impeller/geometry/scalar.h"
1213
#include "impeller/geometry/vector.h"
1314

@@ -127,11 +128,65 @@ Color::Color(const ColorHSB& hsbColor) : Color(hsbColor.ToRGBA()) {}
127128
Color::Color(const Vector4& value)
128129
: red(value.x), green(value.y), blue(value.z), alpha(value.w) {}
129130

130-
Color Min(Color c, float threshold) {
131+
static constexpr Color Min(Color c, float threshold) {
131132
return Color(std::min(c.red, threshold), std::min(c.green, threshold),
132133
std::min(c.blue, threshold), std::min(c.alpha, threshold));
133134
}
134135

136+
// The following HSV utilities correspond to the W3C blend definitions
137+
// implemented in: impeller/compiler/shader_lib/impeller/blending.glsl
138+
139+
static constexpr Scalar Luminosity(Vector3 color) {
140+
return color.x * 0.3 + color.y * 0.59 + color.z * 0.11;
141+
}
142+
143+
static constexpr Vector3 ClipColor(Vector3 color) {
144+
Scalar lum = Luminosity(color);
145+
Scalar mn = std::min(std::min(color.x, color.y), color.z);
146+
Scalar mx = std::max(std::max(color.x, color.y), color.z);
147+
if (mn < 0.0) {
148+
color = lum + (((color - lum) * lum) / (lum - mn + kEhCloseEnough));
149+
}
150+
if (mx > 1.0) {
151+
color = lum + (((color - lum) * (1.0 - lum)) / (mx - lum + kEhCloseEnough));
152+
}
153+
return Vector3();
154+
}
155+
156+
static constexpr Vector3 SetLuminosity(Vector3 color, Scalar luminosity) {
157+
Scalar relative_lum = luminosity - Luminosity(color);
158+
return ClipColor(color + relative_lum);
159+
}
160+
161+
static constexpr Scalar Saturation(Vector3 color) {
162+
return std::max(std::max(color.x, color.y), color.z) -
163+
std::min(std::min(color.x, color.y), color.z);
164+
}
165+
166+
static constexpr Vector3 SetSaturation(Vector3 color, Scalar saturation) {
167+
Scalar mn = std::min(std::min(color.x, color.y), color.z);
168+
Scalar mx = std::max(std::max(color.x, color.y), color.z);
169+
return (mn < mx) ? ((color - mn) * saturation) / (mx - mn) : Vector3();
170+
}
171+
172+
static constexpr Vector3 ComponentChoose(Vector3 a,
173+
Vector3 b,
174+
Vector3 value,
175+
Scalar cutoff) {
176+
return Vector3(value.x > cutoff ? b.x : a.x, //
177+
value.y > cutoff ? b.y : a.y, //
178+
value.z > cutoff ? b.z : a.z //
179+
);
180+
}
181+
182+
static constexpr Vector3 ToRGB(Color color) {
183+
return {color.red, color.green, color.blue};
184+
}
185+
186+
static constexpr Color FromRGB(Vector3 color, Scalar alpha) {
187+
return {color.x, color.y, color.z, alpha};
188+
}
189+
135190
Color Color::BlendColor(const Color& src,
136191
const Color& dst,
137192
BlendMode blend_mode) {
@@ -237,6 +292,24 @@ Color Color::BlendColor(const Color& src,
237292
// s.a * d.a - 2 * (d.a - d) * (s.a - s)
238293
return src.alpha * dst.alpha - 2 * (dst.alpha - d) * (src.alpha - s);
239294
});
295+
case BlendMode::kSoftLight: {
296+
Vector3 dst_rgb = ToRGB(dst);
297+
Vector3 src_rgb = ToRGB(src);
298+
Vector3 d = ComponentChoose(
299+
((16.0 * dst_rgb - 12.0) * dst_rgb + 4.0) * dst_rgb, //
300+
Vector3(std::sqrt(dst_rgb.x), std::sqrt(dst_rgb.y),
301+
std::sqrt(dst_rgb.z)), //
302+
dst_rgb, //
303+
0.25);
304+
Color blended =
305+
FromRGB(ComponentChoose(
306+
dst_rgb - (1.0 - 2.0 * src) * dst * (1.0 - dst_rgb), //
307+
dst_rgb + (2.0 * src_rgb - 1.0) * (d - dst_rgb), //
308+
src_rgb, //
309+
0.5),
310+
dst.alpha);
311+
return blended + dst * (1 - blended.alpha);
312+
}
240313
case BlendMode::kDifference:
241314
return apply_rgb_srcover_alpha([&](auto s, auto d) {
242315
// s + d - 2 * min(s * d.a, d * s.a);
@@ -252,13 +325,38 @@ Color Color::BlendColor(const Color& src,
252325
// s * (1 - d.a) + d * (1 - s.a) + (s * d)
253326
return s * (1 - dst.alpha) + d * (1 - src.alpha) + (s * d);
254327
});
255-
case BlendMode::kHue:
256-
case BlendMode::kSaturation:
257-
case BlendMode::kColor:
258-
case BlendMode::kLuminosity:
259-
case BlendMode::kSoftLight:
260-
default:
261-
return src + dst * (1 - src.alpha);
328+
case BlendMode::kHue: {
329+
Vector3 dst_rgb = ToRGB(dst);
330+
Vector3 src_rgb = ToRGB(src);
331+
Color blended =
332+
FromRGB(SetLuminosity(SetSaturation(src_rgb, Saturation(dst_rgb)),
333+
Luminosity(dst_rgb)),
334+
dst.alpha);
335+
return blended + dst * (1 - blended.alpha);
336+
}
337+
case BlendMode::kSaturation: {
338+
Vector3 dst_rgb = ToRGB(dst);
339+
Vector3 src_rgb = ToRGB(src);
340+
Color blended =
341+
FromRGB(SetLuminosity(SetSaturation(dst_rgb, Saturation(src_rgb)),
342+
Luminosity(dst_rgb)),
343+
dst.alpha);
344+
return blended + dst * (1 - blended.alpha);
345+
}
346+
case BlendMode::kColor: {
347+
Vector3 dst_rgb = ToRGB(dst);
348+
Vector3 src_rgb = ToRGB(src);
349+
Color blended =
350+
FromRGB(SetLuminosity(src_rgb, Luminosity(dst_rgb)), dst.alpha);
351+
return blended + dst * (1 - blended.alpha);
352+
}
353+
case BlendMode::kLuminosity: {
354+
Vector3 dst_rgb = ToRGB(dst);
355+
Vector3 src_rgb = ToRGB(src);
356+
Color blended =
357+
FromRGB(SetLuminosity(dst_rgb, Luminosity(src_rgb)), dst.alpha);
358+
return blended + dst * (1 - blended.alpha);
359+
}
262360
}
263361
}
264362

impeller/geometry/geometry_unittests.cc

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,37 +1277,69 @@ TEST(GeometryTest, CanPerformAlgebraicVector3Ops) {
12771277

12781278
TEST(GeometryTest, CanPerformAlgebraicVector3OpsWithArithmeticTypes) {
12791279
// LHS
1280+
{
1281+
Vector3 p1(1, 2, 3);
1282+
Vector3 p2 = p1 + 2.0f;
1283+
ASSERT_EQ(p2.x, 3);
1284+
ASSERT_EQ(p2.y, 4);
1285+
ASSERT_EQ(p2.z, 5);
1286+
}
1287+
1288+
{
1289+
Vector3 p1(1, 2, 3);
1290+
Vector3 p2 = p1 - 2.0f;
1291+
ASSERT_EQ(p2.x, -1);
1292+
ASSERT_EQ(p2.y, 0);
1293+
ASSERT_EQ(p2.z, 1);
1294+
}
1295+
12801296
{
12811297
Vector3 p1(1, 2, 3);
12821298
Vector3 p2 = p1 * 2.0f;
1283-
ASSERT_EQ(p2.x, 2u);
1284-
ASSERT_EQ(p2.y, 4u);
1285-
ASSERT_EQ(p2.z, 6u);
1299+
ASSERT_EQ(p2.x, 2);
1300+
ASSERT_EQ(p2.y, 4);
1301+
ASSERT_EQ(p2.z, 6);
12861302
}
12871303

12881304
{
12891305
Vector3 p1(2, 6, 12);
12901306
Vector3 p2 = p1 / 2.0f;
1291-
ASSERT_EQ(p2.x, 1u);
1292-
ASSERT_EQ(p2.y, 3u);
1293-
ASSERT_EQ(p2.z, 6u);
1307+
ASSERT_EQ(p2.x, 1);
1308+
ASSERT_EQ(p2.y, 3);
1309+
ASSERT_EQ(p2.z, 6);
12941310
}
12951311

12961312
// RHS
1313+
{
1314+
Vector3 p1(1, 2, 3);
1315+
Vector3 p2 = 2.0f + p1;
1316+
ASSERT_EQ(p2.x, 3);
1317+
ASSERT_EQ(p2.y, 4);
1318+
ASSERT_EQ(p2.z, 5);
1319+
}
1320+
1321+
{
1322+
Vector3 p1(1, 2, 3);
1323+
Vector3 p2 = 2.0f - p1;
1324+
ASSERT_EQ(p2.x, 1);
1325+
ASSERT_EQ(p2.y, 0);
1326+
ASSERT_EQ(p2.z, -1);
1327+
}
1328+
12971329
{
12981330
Vector3 p1(1, 2, 3);
12991331
Vector3 p2 = 2.0f * p1;
1300-
ASSERT_EQ(p2.x, 2u);
1301-
ASSERT_EQ(p2.y, 4u);
1302-
ASSERT_EQ(p2.z, 6u);
1332+
ASSERT_EQ(p2.x, 2);
1333+
ASSERT_EQ(p2.y, 4);
1334+
ASSERT_EQ(p2.z, 6);
13031335
}
13041336

13051337
{
13061338
Vector3 p1(2, 6, 12);
13071339
Vector3 p2 = 12.0f / p1;
1308-
ASSERT_EQ(p2.x, 6u);
1309-
ASSERT_EQ(p2.y, 2u);
1310-
ASSERT_EQ(p2.z, 1u);
1340+
ASSERT_EQ(p2.x, 6);
1341+
ASSERT_EQ(p2.y, 2);
1342+
ASSERT_EQ(p2.z, 1);
13111343
}
13121344
}
13131345

impeller/geometry/vector.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,14 @@ struct Vector3 {
142142
return Vector3(x - v.x, y - v.y, z - v.z);
143143
}
144144

145+
constexpr Vector3 operator+(Scalar s) const {
146+
return Vector3(x + s, y + s, z + s);
147+
}
148+
149+
constexpr Vector3 operator-(Scalar s) const {
150+
return Vector3(x - s, y - s, z - s);
151+
}
152+
145153
constexpr Vector3 operator*(const Vector3& v) const {
146154
return Vector3(x * v.x, y * v.y, z * v.z);
147155
}
@@ -195,6 +203,16 @@ constexpr Vector3 operator*(U s, const Vector3& p) {
195203
return p * s;
196204
}
197205

206+
template <class U, class = std::enable_if_t<std::is_arithmetic_v<U>>>
207+
constexpr Vector3 operator+(U s, const Vector3& p) {
208+
return p + s;
209+
}
210+
211+
template <class U, class = std::enable_if_t<std::is_arithmetic_v<U>>>
212+
constexpr Vector3 operator-(U s, const Vector3& p) {
213+
return -p + s;
214+
}
215+
198216
template <class U, class = std::enable_if_t<std::is_arithmetic_v<U>>>
199217
constexpr Vector3 operator/(U s, const Vector3& p) {
200218
return {

0 commit comments

Comments
 (0)