|
18 | 18 | #include "flutter/display_list/dl_paint.h"
|
19 | 19 | #include "flutter/impeller/display_list/dl_image_impeller.h"
|
20 | 20 | #include "flutter/impeller/geometry/scalar.h"
|
| 21 | +#include "impeller/aiks/aiks_context.h" |
| 22 | +#include "impeller/display_list/dl_dispatcher.h" |
| 23 | +#include "impeller/playground/playground.h" |
| 24 | +#include "impeller/playground/playground_test.h" |
21 | 25 | #include "include/core/SkMatrix.h"
|
22 | 26 |
|
23 | 27 | ////////////////////////////////////////////////////////////////////////////////
|
@@ -566,5 +570,122 @@ TEST_P(AiksTest, FramebufferAdvancedBlendCoverage) {
|
566 | 570 | ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
567 | 571 | }
|
568 | 572 |
|
| 573 | +TEST_P(AiksTest, ColorWheel) { |
| 574 | + // Compare with https://fiddle.skia.org/c/@BlendModes |
| 575 | + |
| 576 | + BlendModeSelection blend_modes = GetBlendModeSelection(); |
| 577 | + |
| 578 | + auto draw_color_wheel = [](DisplayListBuilder& builder) -> void { |
| 579 | + /// color_wheel_sampler: r=0 -> fuchsia, r=2pi/3 -> yellow, r=4pi/3 -> |
| 580 | + /// cyan domain: r >= 0 (because modulo used is non euclidean) |
| 581 | + auto color_wheel_sampler = [](Radians r) { |
| 582 | + Scalar x = r.radians / k2Pi + 1; |
| 583 | + |
| 584 | + // https://www.desmos.com/calculator/6nhjelyoaj |
| 585 | + auto color_cycle = [](Scalar x) { |
| 586 | + Scalar cycle = std::fmod(x, 6.0f); |
| 587 | + return std::max(0.0f, std::min(1.0f, 2 - std::abs(2 - cycle))); |
| 588 | + }; |
| 589 | + return Color(color_cycle(6 * x + 1), // |
| 590 | + color_cycle(6 * x - 1), // |
| 591 | + color_cycle(6 * x - 3), // |
| 592 | + 1); |
| 593 | + }; |
| 594 | + |
| 595 | + DlPaint paint; |
| 596 | + paint.setBlendMode(DlBlendMode::kSrcOver); |
| 597 | + |
| 598 | + // Draw a fancy color wheel for the backdrop. |
| 599 | + // https://www.desmos.com/calculator/xw7kafthwd |
| 600 | + const int max_dist = 900; |
| 601 | + for (int i = 0; i <= 900; i++) { |
| 602 | + Radians r(kPhi / k2Pi * i); |
| 603 | + Scalar distance = r.radians / std::powf(4.12, 0.0026 * r.radians); |
| 604 | + Scalar normalized_distance = static_cast<Scalar>(i) / max_dist; |
| 605 | + |
| 606 | + auto color = color_wheel_sampler(r).WithAlpha(1.0f - normalized_distance); |
| 607 | + paint.setColor( |
| 608 | + DlColor::RGBA(color.red, color.green, color.blue, color.alpha)); |
| 609 | + SkPoint position = SkPoint::Make(distance * std::sin(r.radians), |
| 610 | + -distance * std::cos(r.radians)); |
| 611 | + |
| 612 | + builder.DrawCircle(position, 9 + normalized_distance * 3, paint); |
| 613 | + } |
| 614 | + }; |
| 615 | + |
| 616 | + auto callback = [&]() -> sk_sp<DisplayList> { |
| 617 | + // UI state. |
| 618 | + static bool cache_the_wheel = true; |
| 619 | + static int current_blend_index = 3; |
| 620 | + static float dst_alpha = 1; |
| 621 | + static float src_alpha = 1; |
| 622 | + static DlColor color0 = DlColor::kRed(); |
| 623 | + static DlColor color1 = DlColor::kGreen(); |
| 624 | + static DlColor color2 = DlColor::kBlue(); |
| 625 | + |
| 626 | + if (AiksTest::ImGuiBegin("Controls", nullptr, |
| 627 | + ImGuiWindowFlags_AlwaysAutoResize)) { |
| 628 | + ImGui::Checkbox("Cache the wheel", &cache_the_wheel); |
| 629 | + ImGui::ListBox("Blending mode", ¤t_blend_index, |
| 630 | + blend_modes.blend_mode_names.data(), |
| 631 | + blend_modes.blend_mode_names.size()); |
| 632 | + ImGui::SliderFloat("Source alpha", &src_alpha, 0, 1); |
| 633 | + ImGui::ColorEdit4("Color A", reinterpret_cast<float*>(&color0)); |
| 634 | + ImGui::ColorEdit4("Color B", reinterpret_cast<float*>(&color1)); |
| 635 | + ImGui::ColorEdit4("Color C", reinterpret_cast<float*>(&color2)); |
| 636 | + ImGui::SliderFloat("Destination alpha", &dst_alpha, 0, 1); |
| 637 | + ImGui::End(); |
| 638 | + } |
| 639 | + |
| 640 | + DisplayListBuilder builder; |
| 641 | + |
| 642 | + DlPaint paint; |
| 643 | + paint.setColor(DlColor::kWhite().withAlpha(dst_alpha * 255)); |
| 644 | + paint.setBlendMode(DlBlendMode::kSrc); |
| 645 | + builder.SaveLayer(nullptr, &paint); |
| 646 | + { |
| 647 | + DlPaint paint; |
| 648 | + paint.setColor(DlColor::kWhite()); |
| 649 | + builder.DrawPaint(paint); |
| 650 | + |
| 651 | + builder.SaveLayer(nullptr, nullptr); |
| 652 | + builder.Scale(GetContentScale().x, GetContentScale().y); |
| 653 | + builder.Translate(500, 400); |
| 654 | + builder.Scale(3, 3); |
| 655 | + draw_color_wheel(builder); |
| 656 | + builder.Restore(); |
| 657 | + } |
| 658 | + builder.Restore(); |
| 659 | + |
| 660 | + builder.Scale(GetContentScale().x, GetContentScale().y); |
| 661 | + builder.Translate(500, 400); |
| 662 | + builder.Scale(3, 3); |
| 663 | + |
| 664 | + // Draw 3 circles to a subpass and blend it in. |
| 665 | + DlPaint save_paint; |
| 666 | + save_paint.setColor(DlColor::kWhite().withAlpha(src_alpha * 255)); |
| 667 | + save_paint.setBlendMode(static_cast<DlBlendMode>( |
| 668 | + blend_modes.blend_mode_values[current_blend_index])); |
| 669 | + builder.SaveLayer(nullptr, &save_paint); |
| 670 | + { |
| 671 | + DlPaint paint; |
| 672 | + paint.setBlendMode(DlBlendMode::kPlus); |
| 673 | + const Scalar x = std::sin(k2Pi / 3); |
| 674 | + const Scalar y = -std::cos(k2Pi / 3); |
| 675 | + paint.setColor(color0); |
| 676 | + builder.DrawCircle(SkPoint::Make(-x * 45, y * 45), 65, paint); |
| 677 | + paint.setColor(color1); |
| 678 | + builder.DrawCircle(SkPoint::Make(0, -45), 65, paint); |
| 679 | + paint.setColor(color2); |
| 680 | + builder.DrawCircle(SkPoint::Make(x * 45, y * 45), 65, paint); |
| 681 | + } |
| 682 | + builder.Restore(); |
| 683 | + |
| 684 | + return builder.Build(); |
| 685 | + }; |
| 686 | + |
| 687 | + ASSERT_TRUE(OpenPlaygroundHere(callback)); |
| 688 | +} |
| 689 | + |
569 | 690 | } // namespace testing
|
570 | 691 | } // namespace impeller
|
0 commit comments