diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 9e36f2e3ed9a8..817f56aae227a 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -2208,6 +2208,9 @@ static Picture BlendModeTest(Vector2 content_scale, Canvas canvas; canvas.DrawPaint({.color = Color::Black()}); + // TODO(bdero): Why does this cause the left image to double scale on high DPI + // displays. + // canvas.Scale(content_scale); //---------------------------------------------------------------------------- /// 1. Save layer blending (top squares). @@ -2245,7 +2248,6 @@ static Picture BlendModeTest(Vector2 content_scale, canvas.Save(); canvas.Translate({0, 100}); - // Perform the blend in a SaveLayer so that the initial backdrop color is // fully transparent black. SourceOver blend the result onto the parent pass. canvas.SaveLayer({}); @@ -2256,7 +2258,8 @@ static Picture BlendModeTest(Vector2 content_scale, .blend_mode = BlendMode::kSourceOver}); canvas.Translate(Vector2(100, 0)); } - canvas.RestoreToCount(0); + canvas.Restore(); + canvas.Restore(); //---------------------------------------------------------------------------- /// 3. Image blending (bottom images). @@ -2278,16 +2281,17 @@ static Picture BlendModeTest(Vector2 content_scale, } } - // Uploaded image source (unpremultiplied source texture). + // Uploaded image source (left image). canvas.Save(); canvas.SaveLayer({.blend_mode = BlendMode::kSourceOver}); { canvas.DrawImage(dst_image, {0, 0}, {.blend_mode = BlendMode::kSourceOver}); canvas.DrawImage(src_image, {0, 0}, {.blend_mode = blend_mode}); } - canvas.RestoreToCount(0); + canvas.Restore(); + canvas.Restore(); - // Rendered image source (premultiplied source texture). + // Rendered image source (right image). canvas.Save(); canvas.SaveLayer({.blend_mode = BlendMode::kSourceOver}); { @@ -2301,7 +2305,7 @@ static Picture BlendModeTest(Vector2 content_scale, canvas.Restore(); } canvas.Restore(); - canvas.RestoreToCount(0); + canvas.Restore(); return canvas.EndRecordingAsPicture(); } @@ -3176,7 +3180,7 @@ TEST_P(AiksTest, SolidColorApplyColorFilter) { }); ASSERT_TRUE(result); ASSERT_COLOR_NEAR(contents.GetColor(), - Color(0.433247, 0.879523, 0.825324, 0.75)); + Color(0.424452, 0.828743, 0.79105, 0.9375)); } TEST_P(AiksTest, DrawScaledTextWithPerspectiveNoSaveLayer) { diff --git a/impeller/compiler/shader_lib/impeller/blending.glsl b/impeller/compiler/shader_lib/impeller/blending.glsl index dc2fd1382705e..59352f2bd22d2 100644 --- a/impeller/compiler/shader_lib/impeller/blending.glsl +++ b/impeller/compiler/shader_lib/impeller/blending.glsl @@ -6,9 +6,28 @@ #define BLENDING_GLSL_ #include +#include #include #include +/// Composite a blended color onto the destination. +/// All three parameters are unpremultiplied. Returns a premultiplied result. +/// +/// This routine is the same as `ApplyBlendedColor` in +/// `impeller/geometry/color.cc`. +f16vec4 IPApplyBlendedColor(f16vec4 dst, f16vec4 src, f16vec3 blend_result) { + dst = IPHalfPremultiply(dst); + src = + // Use the blended color for areas where the source and destination + // colors overlap. + IPHalfPremultiply(f16vec4(blend_result, src.a * dst.a)) + + // Use the original source color for any remaining non-overlapping areas. + IPHalfPremultiply(src) * (1.0hf - dst.a); + + // Source-over composite the blended source color atop the destination. + return src + dst * (1.0hf - src.a); +} + //------------------------------------------------------------------------------ /// HSV utilities. /// diff --git a/impeller/entity/shaders/blending/advanced_blend.frag b/impeller/entity/shaders/blending/advanced_blend.frag index b15c5db7fe06d..0e7f80958ccac 100644 --- a/impeller/entity/shaders/blending/advanced_blend.frag +++ b/impeller/entity/shaders/blending/advanced_blend.frag @@ -36,20 +36,20 @@ f16vec4 Sample(f16sampler2D texture_sampler, vec2 texture_coords) { } void main() { - f16vec4 dst_sample = Sample(texture_sampler_dst, // sampler - v_dst_texture_coords // texture coordinates - ) * - blend_info.dst_input_alpha; - - f16vec4 dst = dst_sample; - f16vec4 src = blend_info.color_factor > 0.0hf - ? blend_info.color - : Sample(texture_sampler_src, // sampler - v_src_texture_coords // texture coordinates - ) * - blend_info.src_input_alpha; + f16vec4 dst = + IPHalfUnpremultiply(Sample(texture_sampler_dst, // sampler + v_dst_texture_coords // texture coordinates + )); + dst *= blend_info.dst_input_alpha; + f16vec4 src = IPHalfUnpremultiply( + blend_info.color_factor > 0.0hf + ? blend_info.color + : Sample(texture_sampler_src, // sampler + v_src_texture_coords // texture coordinates + )); + src *= blend_info.src_input_alpha; f16vec3 blend_result = AdvancedBlend(dst.rgb, src.rgb, int(blend_type)); - f16vec4 blended = mix(src, f16vec4(blend_result, src.a), dst.a); - frag_color = mix(dst_sample, blended, src.a); + + frag_color = IPApplyBlendedColor(dst, src, blend_result); } diff --git a/impeller/entity/shaders/blending/framebuffer_blend.frag b/impeller/entity/shaders/blending/framebuffer_blend.frag index 58cedbd56b4c0..095d5e5bd66d0 100644 --- a/impeller/entity/shaders/blending/framebuffer_blend.frag +++ b/impeller/entity/shaders/blending/framebuffer_blend.frag @@ -55,13 +55,14 @@ vec4 Sample(sampler2D texture_sampler, vec2 texture_coords) { } void main() { - f16vec4 dst = f16vec4(ReadDestination()); - f16vec4 src = f16vec4(Sample(texture_sampler_src, // sampler - v_src_texture_coords // texture coordinates - )) * - frag_info.src_input_alpha; + f16vec4 dst = IPHalfUnpremultiply(f16vec4(ReadDestination())); + f16vec4 src = IPHalfUnpremultiply( + f16vec4(Sample(texture_sampler_src, // sampler + v_src_texture_coords // texture coordinates + ))); + src.a *= frag_info.src_input_alpha; f16vec3 blend_result = AdvancedBlend(dst.rgb, src.rgb, int(blend_type)); - f16vec4 blended = mix(src, f16vec4(blend_result, src.a), dst.a); - frag_color = vec4(mix(dst, blended, src.a)); + + frag_color = IPApplyBlendedColor(dst, src, blend_result); } diff --git a/impeller/geometry/color.cc b/impeller/geometry/color.cc index 2efe123c906f4..d2565e6e6fd1d 100644 --- a/impeller/geometry/color.cc +++ b/impeller/geometry/color.cc @@ -193,30 +193,42 @@ static constexpr inline Color FromRGB(Vector3 color, Scalar alpha) { return {color.x, color.y, color.z, alpha}; } +/// Composite a blended color onto the destination. +/// All three parameters are unpremultiplied. Returns a premultiplied result. +/// +/// This routine is the same as `IPApplyBlendedColor` in the Impeller shader +/// library. +static constexpr inline Color ApplyBlendedColor(Color dst, + Color src, + Vector3 blend_result) { + dst = dst.Premultiply(); + src = + // Use the blended color for areas where the source and destination + // colors overlap. + FromRGB(blend_result, src.alpha * dst.alpha).Premultiply() + + // Use the original source color for any remaining non-overlapping areas. + src.Premultiply() * (1.0f - dst.alpha); + + // Source-over composite the blended source color atop the destination. + return src + dst * (1.0f - src.alpha); +} + static constexpr inline Color DoColorBlend( - Color d, - Color s, + Color dst, + Color src, const std::function& blend_rgb_func) { - d = d.Premultiply(); - s = s.Premultiply(); - const Vector3 rgb = blend_rgb_func(ToRGB(d), ToRGB(s)); - const Color blended = Color::Lerp(s, FromRGB(rgb, d.alpha), d.alpha); - return Color::Lerp(d, blended, s.alpha).Unpremultiply(); + const Vector3 blend_result = blend_rgb_func(ToRGB(dst), ToRGB(src)); + return ApplyBlendedColor(dst, src, blend_result).Unpremultiply(); } static constexpr inline Color DoColorBlendComponents( - Color d, - Color s, + Color dst, + Color src, const std::function& blend_func) { - d = d.Premultiply(); - s = s.Premultiply(); - const Color blended = Color::Lerp(s, - Color(blend_func(d.red, s.red), // - blend_func(d.green, s.green), // - blend_func(d.blue, s.blue), // - d.alpha), - d.alpha); - return Color::Lerp(d, blended, s.alpha).Unpremultiply(); + Vector3 blend_result = Vector3(blend_func(dst.red, src.red), // + blend_func(dst.green, src.green), // + blend_func(dst.blue, src.blue)); // + return ApplyBlendedColor(dst, src, blend_result).Unpremultiply(); } Color Color::Blend(Color src, BlendMode blend_mode) const { diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 6b7f6951c8502..d9df8d1bf0893 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -1431,6 +1431,158 @@ TEST(GeometryTest, ColorSRGBToLinear) { } } +struct ColorBlendTestData { + static constexpr Color kDestinationColor = + Color::CornflowerBlue().WithAlpha(0.75); + static constexpr Color kSourceColors[] = {Color::White().WithAlpha(0.75), + Color::LimeGreen().WithAlpha(0.75), + Color::Black().WithAlpha(0.75)}; + + // THIS RESULT TABLE IS GENERATED! + // + // Uncomment the `GenerateColorBlendResults` test below to print a new table + // after making changes to `Color::Blend`. + static constexpr Color kExpectedResults + [sizeof(kSourceColors)] + [static_cast>(BlendMode::kLast) + 1] = { + { + {0, 0, 0, 0}, // Clear + {1, 1, 1, 0.75}, // Source + {0.392157, 0.584314, 0.929412, 0.75}, // Destination + {0.878431, 0.916863, 0.985882, 0.9375}, // SourceOver + {0.513726, 0.667451, 0.943529, 0.9375}, // DestinationOver + {1, 1, 1, 0.5625}, // SourceIn + {0.392157, 0.584314, 0.929412, 0.5625}, // DestinationIn + {1, 1, 1, 0.1875}, // SourceOut + {0.392157, 0.584314, 0.929412, 0.1875}, // DestinationOut + {0.848039, 0.896078, 0.982353, 0.75}, // SourceATop + {0.544118, 0.688235, 0.947059, 0.75}, // DestinationATop + {0.696078, 0.792157, 0.964706, 0.375}, // Xor + {1, 1, 1, 1}, // Plus + {0.392157, 0.584314, 0.929412, 0.5625}, // Modulate + {0.878431, 0.916863, 0.985882, 0.9375}, // Screen + {0.74902, 0.916863, 0.985882, 0.9375}, // Overlay + {0.513726, 0.667451, 0.943529, 0.9375}, // Darken + {0.878431, 0.916863, 0.985882, 0.9375}, // Lighten + {0.878431, 0.916863, 0.985882, 0.9375}, // ColorDodge + {0.513725, 0.667451, 0.943529, 0.9375}, // ColorBurn + {0.878431, 0.916863, 0.985882, 0.9375}, // HardLight + {0.654166, 0.775505, 0.964318, 0.9375}, // SoftLight + {0.643137, 0.566275, 0.428235, 0.9375}, // Difference + {0.643137, 0.566275, 0.428235, 0.9375}, // Exclusion + {0.513726, 0.667451, 0.943529, 0.9375}, // Multiply + {0.617208, 0.655639, 0.724659, 0.9375}, // Hue + {0.617208, 0.655639, 0.724659, 0.9375}, // Saturation + {0.617208, 0.655639, 0.724659, 0.9375}, // Color + {0.878431, 0.916863, 0.985882, 0.9375}, // Luminosity + }, + { + {0, 0, 0, 0}, // Clear + {0.196078, 0.803922, 0.196078, 0.75}, // Source + {0.392157, 0.584314, 0.929412, 0.75}, // Destination + {0.235294, 0.76, 0.342745, 0.9375}, // SourceOver + {0.352941, 0.628235, 0.782745, 0.9375}, // DestinationOver + {0.196078, 0.803922, 0.196078, 0.5625}, // SourceIn + {0.392157, 0.584314, 0.929412, 0.5625}, // DestinationIn + {0.196078, 0.803922, 0.196078, 0.1875}, // SourceOut + {0.392157, 0.584314, 0.929412, 0.1875}, // DestinationOut + {0.245098, 0.74902, 0.379412, 0.75}, // SourceATop + {0.343137, 0.639216, 0.746078, 0.75}, // DestinationATop + {0.294118, 0.694118, 0.562745, 0.375}, // Xor + {0.441176, 1, 0.844118, 1}, // Plus + {0.0768935, 0.469742, 0.182238, 0.5625}, // Modulate + {0.424452, 0.828743, 0.79105, 0.9375}, // Screen + {0.209919, 0.779839, 0.757001, 0.9375}, // Overlay + {0.235294, 0.628235, 0.342745, 0.9375}, // Darken + {0.352941, 0.76, 0.782745, 0.9375}, // Lighten + {0.41033, 0.877647, 0.825098, 0.9375}, // ColorDodge + {0.117647, 0.567403, 0.609098, 0.9375}, // ColorBurn + {0.209919, 0.779839, 0.443783, 0.9375}, // HardLight + {0.266006, 0.693915, 0.758818, 0.9375}, // SoftLight + {0.235294, 0.409412, 0.665098, 0.9375}, // Difference + {0.378316, 0.546897, 0.681707, 0.9375}, // Exclusion + {0.163783, 0.559493, 0.334441, 0.9375}, // Multiply + {0.266235, 0.748588, 0.373686, 0.9375}, // Hue + {0.339345, 0.629787, 0.811502, 0.9375}, // Saturation + {0.241247, 0.765953, 0.348698, 0.9375}, // Color + {0.346988, 0.622282, 0.776792, 0.9375}, // Luminosity + }, + { + {0, 0, 0, 0}, // Clear + {0, 0, 0, 0.75}, // Source + {0.392157, 0.584314, 0.929412, 0.75}, // Destination + {0.0784314, 0.116863, 0.185882, 0.9375}, // SourceOver + {0.313726, 0.467451, 0.743529, 0.9375}, // DestinationOver + {0, 0, 0, 0.5625}, // SourceIn + {0.392157, 0.584314, 0.929412, 0.5625}, // DestinationIn + {0, 0, 0, 0.1875}, // SourceOut + {0.392157, 0.584314, 0.929412, 0.1875}, // DestinationOut + {0.0980392, 0.146078, 0.232353, 0.75}, // SourceATop + {0.294118, 0.438235, 0.697059, 0.75}, // DestinationATop + {0.196078, 0.292157, 0.464706, 0.375}, // Xor + {0.294118, 0.438235, 0.697059, 1}, // Plus + {0, 0, 0, 0.5625}, // Modulate + {0.313726, 0.467451, 0.743529, 0.9375}, // Screen + {0.0784314, 0.218039, 0.701176, 0.9375}, // Overlay + {0.0784314, 0.116863, 0.185882, 0.9375}, // Darken + {0.313726, 0.467451, 0.743529, 0.9375}, // Lighten + {0.313726, 0.467451, 0.743529, 0.9375}, // ColorDodge + {0.0784314, 0.116863, 0.185882, 0.9375}, // ColorBurn + {0.0784314, 0.116863, 0.185882, 0.9375}, // HardLight + {0.170704, 0.321716, 0.704166, 0.9375}, // SoftLight + {0.313726, 0.467451, 0.743529, 0.9375}, // Difference + {0.313726, 0.467451, 0.743529, 0.9375}, // Exclusion + {0.0784314, 0.116863, 0.185882, 0.9375}, // Multiply + {0.417208, 0.455639, 0.524659, 0.9375}, // Hue + {0.417208, 0.455639, 0.524659, 0.9375}, // Saturation + {0.417208, 0.455639, 0.524659, 0.9375}, // Color + {0.0784314, 0.116863, 0.185882, 0.9375}, // Luminosity + }, + }; +}; + +/// To print a new ColorBlendTestData::kExpectedResults table, uncomment this +/// test and run with: +/// --gtest_filter="GeometryTest.GenerateColorBlendResults" +/* +TEST(GeometryTest, GenerateColorBlendResults) { + auto& o = std::cout; + using BlendT = std::underlying_type_t; + o << "{"; + for (const auto& source : ColorBlendTestData::kSourceColors) { + o << "{"; + for (BlendT blend_i = 0; + blend_i < static_cast(BlendMode::kLast) + 1; blend_i++) { + auto blend = static_cast(blend_i); + Color c = ColorBlendTestData::kDestinationColor.Blend(source, blend); + o << "{" << c.red << "," << c.green << "," << c.blue << "," << c.alpha + << "}, // " << BlendModeToString(blend) << std::endl; + } + o << "},"; + } + o << "};" << std::endl; +} +*/ + +#define _BLEND_MODE_RESULT_CHECK(blend_mode) \ + blend_i = static_cast(BlendMode::k##blend_mode); \ + expected = ColorBlendTestData::kExpectedResults[source_i][blend_i]; \ + EXPECT_COLOR_NEAR(dst.Blend(src, BlendMode::k##blend_mode), expected); + +TEST(GeometryTest, ColorBlendReturnsExpectedResults) { + using BlendT = std::underlying_type_t; + Color dst = ColorBlendTestData::kDestinationColor; + for (size_t source_i = 0; + source_i < sizeof(ColorBlendTestData::kSourceColors) / sizeof(Color); + source_i++) { + Color src = ColorBlendTestData::kSourceColors[source_i]; + + size_t blend_i; + Color expected; + IMPELLER_FOR_EACH_BLEND_MODE(_BLEND_MODE_RESULT_CHECK) + } +} + #define _BLEND_MODE_NAME_CHECK(blend_mode) \ case BlendMode::k##blend_mode: \ ASSERT_STREQ(result, #blend_mode); \ diff --git a/impeller/tools/malioc.json b/impeller/tools/malioc.json index 377fe50b82df9..8cf4183c4f6ac 100644 --- a/impeller/tools/malioc.json +++ b/impeller/tools/malioc.json @@ -16,14 +16,14 @@ "has_stack_spilling": false, "performance": { "longest_path_bound_pipelines": [ - "varying", - "texture" + "arith_total", + "arith_fma" ], "longest_path_cycles": [ - 0.4375, - 0.4375, - 0.015625, - 0.0, + 0.625, + 0.625, + 0.21875, + 0.125, 0.0, 0.5, 0.5 @@ -42,23 +42,23 @@ "arith_fma" ], "shortest_path_cycles": [ - 0.375, - 0.375, - 0.09375, - 0.0, + 0.625, + 0.625, + 0.234375, + 0.125, 0.0, 0.25, 0.25 ], "total_bound_pipelines": [ - "varying", - "texture" + "arith_total", + "arith_fma" ], "total_cycles": [ - 0.4375, - 0.4375, - 0.09375, - 0.0, + 0.625, + 0.625, + 0.296875, + 0.125, 0.0, 0.5, 0.5 @@ -67,7 +67,7 @@ "stack_spill_bytes": 0, "thread_occupancy": 100, "uniform_registers_used": 10, - "work_registers_used": 16 + "work_registers_used": 24 } } } @@ -1427,14 +1427,14 @@ "has_stack_spilling": false, "performance": { "longest_path_bound_pipelines": [ - "varying", - "texture" + "arith_total", + "arith_fma" ], "longest_path_cycles": [ - 0.4375, - 0.4375, - 0.0625, - 0.0, + 0.625, + 0.625, + 0.265625, + 0.125, 0.0, 0.5, 0.5 @@ -1453,23 +1453,23 @@ "arith_fma" ], "shortest_path_cycles": [ - 0.375, - 0.375, - 0.109375, - 0.0, + 0.625, + 0.625, + 0.25, + 0.125, 0.0, 0.25, 0.25 ], "total_bound_pipelines": [ - "varying", - "texture" + "arith_total", + "arith_fma" ], "total_cycles": [ - 0.4375, - 0.4375, - 0.140625, - 0.0, + 0.625, + 0.625, + 0.34375, + 0.125, 0.0, 0.5, 0.5 @@ -1478,7 +1478,7 @@ "stack_spill_bytes": 0, "thread_occupancy": 100, "uniform_registers_used": 12, - "work_registers_used": 24 + "work_registers_used": 22 } } }, @@ -1495,7 +1495,7 @@ "arithmetic" ], "longest_path_cycles": [ - 2.9700000286102295, + 3.630000114440918, 2.0, 2.0 ], @@ -1508,7 +1508,7 @@ "arithmetic" ], "shortest_path_cycles": [ - 2.640000104904175, + 3.299999952316284, 1.0, 1.0 ], @@ -1516,7 +1516,7 @@ "arithmetic" ], "total_cycles": [ - 3.3333332538604736, + 4.0, 2.0, 2.0 ]