Skip to content

Commit 6f9fa60

Browse files
bderodnfield
authored andcommitted
Add pipeline blend modes & demo (flutter#55)
1 parent 845c3bc commit 6f9fa60

File tree

5 files changed

+170
-8
lines changed

5 files changed

+170
-8
lines changed

impeller/aiks/canvas.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ void Canvas::DrawTextFrame(TextFrame text_frame,
266266
auto text_contents = std::make_shared<TextContents>();
267267
text_contents->SetTextFrame(std::move(text_frame));
268268
text_contents->SetGlyphAtlas(std::move(atlas));
269-
text_contents->SetColor(paint.color);
269+
text_contents->SetColor(paint.color.Premultiply());
270270

271271
Entity entity;
272272
entity.SetTransformation(GetCurrentTransformation() *

impeller/aiks/paint.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ std::shared_ptr<Contents> Paint::CreateContentsForEntity() const {
1616
switch (style) {
1717
case Style::kFill: {
1818
auto solid_color = std::make_shared<SolidColorContents>();
19-
solid_color->SetColor(color);
19+
solid_color->SetColor(color.Premultiply());
2020
return solid_color;
2121
}
2222
case Style::kStroke: {
2323
auto solid_stroke = std::make_shared<SolidStrokeContents>();
24-
solid_stroke->SetColor(color);
24+
solid_stroke->SetColor(color.Premultiply());
2525
solid_stroke->SetStrokeSize(stroke_width);
2626
return solid_stroke;
2727
}

impeller/entity/contents/content_context.h

+43-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#include "flutter/impeller/entity/solid_stroke.vert.h"
2020
#include "flutter/impeller/entity/texture_fill.frag.h"
2121
#include "flutter/impeller/entity/texture_fill.vert.h"
22+
#include "impeller/entity/entity.h"
23+
#include "impeller/renderer/formats.h"
2224

2325
namespace impeller {
2426

@@ -38,17 +40,19 @@ using ClipPipeline = PipelineT<SolidFillVertexShader, SolidFillFragmentShader>;
3840

3941
struct ContentContextOptions {
4042
SampleCount sample_count = SampleCount::kCount1;
43+
Entity::BlendMode blend_mode = Entity::BlendMode::kSource;
4144

4245
struct Hash {
4346
constexpr std::size_t operator()(const ContentContextOptions& o) const {
44-
return fml::HashCombine(o.sample_count);
47+
return fml::HashCombine(o.sample_count, o.blend_mode);
4548
}
4649
};
4750

4851
struct Equal {
4952
constexpr bool operator()(const ContentContextOptions& lhs,
5053
const ContentContextOptions& rhs) const {
51-
return lhs.sample_count == rhs.sample_count;
54+
return lhs.sample_count == rhs.sample_count &&
55+
lhs.blend_mode == rhs.blend_mode;
5256
}
5357
};
5458
};
@@ -120,6 +124,43 @@ class ContentContext {
120124
static void ApplyOptionsToDescriptor(PipelineDescriptor& desc,
121125
const ContentContextOptions& options) {
122126
desc.SetSampleCount(options.sample_count);
127+
128+
ColorAttachmentDescriptor color0 = *desc.GetColorAttachmentDescriptor(0u);
129+
color0.alpha_blend_op = BlendOperation::kAdd;
130+
color0.color_blend_op = BlendOperation::kAdd;
131+
switch (options.blend_mode) {
132+
case Entity::BlendMode::kClear:
133+
color0.dst_alpha_blend_factor = BlendFactor::kZero;
134+
color0.dst_color_blend_factor = BlendFactor::kZero;
135+
color0.src_alpha_blend_factor = BlendFactor::kZero;
136+
color0.src_color_blend_factor = BlendFactor::kZero;
137+
break;
138+
case Entity::BlendMode::kSource:
139+
color0.dst_alpha_blend_factor = BlendFactor::kZero;
140+
color0.dst_color_blend_factor = BlendFactor::kZero;
141+
color0.src_alpha_blend_factor = BlendFactor::kSourceAlpha;
142+
color0.src_color_blend_factor = BlendFactor::kOne;
143+
break;
144+
case Entity::BlendMode::kDestination:
145+
color0.dst_alpha_blend_factor = BlendFactor::kDestinationAlpha;
146+
color0.dst_color_blend_factor = BlendFactor::kOne;
147+
color0.src_alpha_blend_factor = BlendFactor::kZero;
148+
color0.src_color_blend_factor = BlendFactor::kZero;
149+
break;
150+
case Entity::BlendMode::kSourceOver:
151+
color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha;
152+
color0.dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha;
153+
color0.src_alpha_blend_factor = BlendFactor::kSourceAlpha;
154+
color0.src_color_blend_factor = BlendFactor::kOne;
155+
break;
156+
case Entity::BlendMode::kDestinationOver:
157+
color0.dst_alpha_blend_factor = BlendFactor::kDestinationAlpha;
158+
color0.dst_color_blend_factor = BlendFactor::kOne;
159+
color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha;
160+
color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha;
161+
break;
162+
}
163+
desc.SetColorAttachmentDescriptor(0u, std::move(color0));
123164
}
124165

125166
template <class TypedPipeline>

impeller/entity/entity.h

+11
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@ class RenderPass;
1818

1919
class Entity {
2020
public:
21+
/// All pipeline blend mode presets assume that both the source (fragment
22+
/// output) and destination (first color attachment) have colors with
23+
/// premultiplied alpha.
24+
enum class BlendMode {
25+
kClear,
26+
kSource,
27+
kDestination,
28+
kSourceOver,
29+
kDestinationOver,
30+
};
31+
2132
Entity();
2233

2334
~Entity();

impeller/entity/entity_unittests.cc

+113-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include "impeller/geometry/path_builder.h"
1111
#include "impeller/playground/playground.h"
1212
#include "impeller/playground/widgets.h"
13+
#include "impeller/renderer/render_pass.h"
14+
#include "impeller/renderer/vertex_buffer_builder.h"
1315
#include "third_party/imgui/imgui.h"
1416

1517
namespace impeller {
@@ -42,7 +44,7 @@ TEST_F(EntityTest, ThreeStrokesInOnePath) {
4244
Entity entity;
4345
entity.SetPath(path);
4446
auto contents = std::make_unique<SolidStrokeContents>();
45-
contents->SetColor(Color::Red());
47+
contents->SetColor(Color::Red().Premultiply());
4648
contents->SetStrokeSize(5.0);
4749
entity.SetContents(std::move(contents));
4850
ASSERT_TRUE(OpenPlaygroundHere(entity));
@@ -72,7 +74,7 @@ TEST_F(EntityTest, TriangleInsideASquare) {
7274
Entity entity;
7375
entity.SetPath(path);
7476
auto contents = std::make_unique<SolidStrokeContents>();
75-
contents->SetColor(Color::Red());
77+
contents->SetColor(Color::Red().Premultiply());
7678
contents->SetStrokeSize(20.0);
7779
entity.SetContents(std::move(contents));
7880

@@ -110,7 +112,7 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) {
110112
auto create_contents = [width = width](SolidStrokeContents::Cap cap,
111113
SolidStrokeContents::Join join) {
112114
auto contents = std::make_unique<SolidStrokeContents>();
113-
contents->SetColor(Color::Red());
115+
contents->SetColor(Color::Red().Premultiply());
114116
contents->SetStrokeSize(width);
115117
contents->SetStrokeCap(cap);
116118
contents->SetStrokeJoin(join);
@@ -491,5 +493,113 @@ TEST_F(EntityTest, SolidStrokeContentsSetMiter) {
491493
ASSERT_FLOAT_EQ(contents.GetStrokeMiter(), 8);
492494
}
493495

496+
TEST_F(EntityTest, BlendingModeOptions) {
497+
std::vector<const char*> blend_mode_names;
498+
std::vector<Entity::BlendMode> blend_mode_values;
499+
{
500+
// Force an exhausiveness check with a switch. When adding blend modes,
501+
// update this switch with a new name/value to to make it selectable in the
502+
// test GUI.
503+
504+
const Entity::BlendMode b{};
505+
static_assert(
506+
b == Entity::BlendMode::kClear); // Ensure the first item in
507+
// the switch is the first
508+
// item in the enum.
509+
switch (b) {
510+
case Entity::BlendMode::kClear:
511+
blend_mode_names.push_back("Clear");
512+
blend_mode_values.push_back(Entity::BlendMode::kClear);
513+
case Entity::BlendMode::kSource:
514+
blend_mode_names.push_back("Source");
515+
blend_mode_values.push_back(Entity::BlendMode::kSource);
516+
case Entity::BlendMode::kDestination:
517+
blend_mode_names.push_back("Destination");
518+
blend_mode_values.push_back(Entity::BlendMode::kDestination);
519+
case Entity::BlendMode::kSourceOver:
520+
blend_mode_names.push_back("SourceOver");
521+
blend_mode_values.push_back(Entity::BlendMode::kSourceOver);
522+
case Entity::BlendMode::kDestinationOver:
523+
blend_mode_names.push_back("DestinationOver");
524+
blend_mode_values.push_back(
525+
Entity::BlendMode::kDestinationOver);
526+
};
527+
}
528+
529+
bool first_frame = true;
530+
auto callback = [&](ContentContext& context, RenderPass& pass) {
531+
if (first_frame) {
532+
first_frame = false;
533+
ImGui::SetNextWindowSize({350, 200});
534+
ImGui::SetNextWindowPos({200, 450});
535+
}
536+
537+
auto draw_rect = [&context, &pass](
538+
Rect rect, Color color,
539+
Entity::BlendMode blend_mode) -> bool {
540+
using VS = SolidFillPipeline::VertexShader;
541+
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
542+
{
543+
auto r = rect.GetLTRB();
544+
vtx_builder.AddVertices({
545+
{Point(r[0], r[1])},
546+
{Point(r[2], r[1])},
547+
{Point(r[2], r[3])},
548+
{Point(r[0], r[1])},
549+
{Point(r[2], r[3])},
550+
{Point(r[0], r[3])},
551+
});
552+
}
553+
554+
Command cmd;
555+
cmd.label = "Blended Rectangle";
556+
auto options = OptionsFromPass(pass);
557+
options.blend_mode = blend_mode;
558+
cmd.pipeline = context.GetSolidFillPipeline(options);
559+
cmd.BindVertices(
560+
vtx_builder.CreateVertexBuffer(pass.GetTransientsBuffer()));
561+
562+
VS::FrameInfo frame_info;
563+
frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize());
564+
frame_info.color = color.Premultiply();
565+
VS::BindFrameInfo(cmd,
566+
pass.GetTransientsBuffer().EmplaceUniform(frame_info));
567+
568+
cmd.primitive_type = PrimitiveType::kTriangle;
569+
570+
return pass.AddCommand(std::move(cmd));
571+
};
572+
573+
ImGui::Begin("Controls");
574+
static Color color1(1, 0, 0, 0.5), color2(0, 1, 0, 0.5);
575+
ImGui::ColorEdit4("Color 1", reinterpret_cast<float*>(&color1));
576+
ImGui::ColorEdit4("Color 2", reinterpret_cast<float*>(&color2));
577+
static int current_blend_index = 3;
578+
ImGui::ListBox("Blending mode", &current_blend_index,
579+
blend_mode_names.data(), blend_mode_names.size());
580+
ImGui::End();
581+
582+
Entity::BlendMode selected_mode =
583+
blend_mode_values[current_blend_index];
584+
585+
Point a, b, c, d;
586+
std::tie(a, b) = IMPELLER_PLAYGROUND_LINE(
587+
Point(400, 100), Point(200, 300), 20, Color::White(), Color::White());
588+
std::tie(c, d) = IMPELLER_PLAYGROUND_LINE(
589+
Point(470, 190), Point(270, 390), 20, Color::White(), Color::White());
590+
591+
bool result = true;
592+
result = result && draw_rect(Rect(0, 0, pass.GetRenderTargetSize().width,
593+
pass.GetRenderTargetSize().height),
594+
Color(), Entity::BlendMode::kClear);
595+
result = result && draw_rect(Rect::MakeLTRB(a.x, a.y, b.x, b.y), color1,
596+
Entity::BlendMode::kSourceOver);
597+
result = result && draw_rect(Rect::MakeLTRB(c.x, c.y, d.x, d.y), color2,
598+
selected_mode);
599+
return result;
600+
};
601+
ASSERT_TRUE(OpenPlaygroundHere(callback));
602+
}
603+
494604
} // namespace testing
495605
} // namespace impeller

0 commit comments

Comments
 (0)