Rework canvas passes in prep for subpasses.

This commit is contained in:
Chinmay Garde 2021-11-22 14:34:24 -08:00 committed by Dan Field
parent 2eb6372d9d
commit 0a855c21f7
13 changed files with 154 additions and 39 deletions

View File

@ -103,6 +103,30 @@ TEST_F(AiksTest, CanRenderClips) {
canvas.ClipPath(
PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 500, 500)).CreatePath());
canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).CreatePath(), paint);
// ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_F(AiksTest, CanRenderGroupOpacity) {
Canvas canvas;
Paint red;
red.color = Color::Red(); //.WithAlpha(0.5);
Paint green;
green.color = Color::Green(); //.WithAlpha(0.5);
Paint blue;
blue.color = Color::Blue(); //.WithAlpha(0.5);
Paint alpha;
alpha.color = Color::Red().WithAlpha(0.5);
canvas.SaveLayer(alpha);
canvas.DrawRect({100, 100, 100, 100}, red);
canvas.DrawRect({120, 120, 100, 100}, green);
canvas.DrawRect({140, 140, 100, 100}, blue);
canvas.Restore();
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

View File

@ -12,15 +12,13 @@
namespace impeller {
Canvas::Canvas() {
xformation_stack_.push({});
passes_.emplace_back(CanvasPass{});
Save(true);
}
Canvas::~Canvas() = default;
void Canvas::Save() {
FML_DCHECK(xformation_stack_.size() > 0);
xformation_stack_.push(xformation_stack_.top());
Save(false);
}
bool Canvas::Restore() {
@ -28,17 +26,16 @@ bool Canvas::Restore() {
if (xformation_stack_.size() == 1) {
return false;
}
xformation_stack_.pop();
xformation_stack_.pop_back();
return true;
}
void Canvas::Concat(const Matrix& xformation) {
const auto current_xformation = xformation_stack_.top().xformation;
xformation_stack_.top().xformation = xformation * current_xformation;
xformation_stack_.back().xformation = xformation * GetCurrentTransformation();
}
const Matrix& Canvas::GetCurrentTransformation() const {
return xformation_stack_.top().xformation;
return xformation_stack_.back().xformation;
}
void Canvas::Translate(const Vector3& offset) {
@ -71,6 +68,7 @@ void Canvas::DrawPath(Path path, Paint paint) {
entity.SetPath(std::move(path));
entity.SetStencilDepth(GetStencilDepth());
entity.SetContents(paint.CreateContentsForEntity());
GetCurrentPass().PushEntity(std::move(entity));
}
@ -86,21 +84,23 @@ void Canvas::ClipPath(Path path) {
entity.SetPath(std::move(path));
entity.SetContents(std::make_shared<ClipContents>());
entity.SetStencilDepth(GetStencilDepth());
GetCurrentPass().PushEntity(std::move(entity));
}
void Canvas::DrawShadow(Path path, Color color, Scalar elevation) {}
void Canvas::DrawPicture(const Picture& picture) {
for (const auto& pass : picture.passes) {
CanvasPass new_pass;
for (const auto& entity : pass.GetPassEntities()) {
auto new_entity = entity;
new_entity.SetTransformation(GetCurrentTransformation() *
entity.GetTransformation());
new_pass.PushEntity(std::move(new_entity));
for (const auto& stack_entry : picture.entries) {
auto new_stack_entry = stack_entry;
if (auto pass = new_stack_entry.pass) {
for (auto entity : pass->GetPassEntities()) {
entity.IncrementStencilDepth(GetStencilDepth());
entity.SetTransformation(GetCurrentTransformation() *
entity.GetTransformation());
}
}
passes_.emplace_back(std::move(new_pass));
xformation_stack_.emplace_back(std::move(new_stack_entry));
}
}
@ -145,21 +145,49 @@ void Canvas::DrawImageRect(std::shared_ptr<Image> image,
Picture Canvas::EndRecordingAsPicture() {
Picture picture;
picture.passes = std::move(passes_);
picture.entries = std::move(xformation_stack_);
return picture;
}
CanvasPass& Canvas::GetCurrentPass() {
FML_DCHECK(!passes_.empty());
return passes_.back();
for (auto i = xformation_stack_.rbegin(), end = xformation_stack_.rend();
i < end; i++) {
if (i->pass.has_value()) {
return i->pass.value();
}
}
FML_UNREACHABLE();
}
void Canvas::IncrementStencilDepth() {
++xformation_stack_.top().stencil_depth;
++xformation_stack_.back().stencil_depth;
}
size_t Canvas::GetStencilDepth() const {
return xformation_stack_.top().stencil_depth;
return xformation_stack_.back().stencil_depth;
}
void Canvas::DrawRect(Rect rect, Paint paint) {
DrawPath(PathBuilder{}.AddRect(rect).CreatePath(), std::move(paint));
}
void Canvas::Save(bool create_subpass) {
// Check if called from the ctor.
if (xformation_stack_.empty()) {
FML_DCHECK(create_subpass) << "Base entries must have a pass.";
CanvasStackEntry entry;
entry.pass = CanvasPass{};
xformation_stack_.emplace_back(std::move(entry));
}
auto entry = CanvasStackEntry{};
entry.xformation = xformation_stack_.back().xformation;
entry.stencil_depth = xformation_stack_.back().stencil_depth;
if (create_subpass) {
entry.pass = CanvasPass{};
}
xformation_stack_.emplace_back(std::move(entry));
}
} // namespace impeller

View File

@ -4,9 +4,9 @@
#pragma once
#include <deque>
#include <memory>
#include <optional>
#include <stack>
#include <vector>
#include "flutter/fml/macros.h"
@ -23,11 +23,6 @@ namespace impeller {
class Entity;
struct CanvasStackEntry {
Matrix xformation;
size_t stencil_depth = 0u;
};
class Canvas {
public:
Canvas();
@ -56,6 +51,8 @@ class Canvas {
void DrawPath(Path path, Paint paint);
void DrawRect(Rect rect, Paint paint);
void DrawImage(std::shared_ptr<Image> image, Point offset, Paint paint);
void DrawImageRect(std::shared_ptr<Image> image,
@ -72,8 +69,7 @@ class Canvas {
Picture EndRecordingAsPicture();
private:
std::stack<CanvasStackEntry> xformation_stack_;
std::vector<CanvasPass> passes_;
std::deque<CanvasStackEntry> xformation_stack_;
CanvasPass& GetCurrentPass();
@ -81,6 +77,8 @@ class Canvas {
size_t GetStencilDepth() const;
void Save(bool create_subpass);
FML_DISALLOW_COPY_AND_ASSIGN(Canvas);
};

View File

@ -26,4 +26,27 @@ const Entity& CanvasPass::GetPostProcessingEntity() const {
return post_processing_entity_;
}
Rect CanvasPass::GetCoverageRect() const {
std::optional<Point> min, max;
for (const auto& entity : ops_) {
auto coverage = entity.GetPath().GetMinMaxCoveragePoints();
if (!coverage.has_value()) {
continue;
}
if (!min.has_value()) {
min = coverage->first;
}
if (!max.has_value()) {
max = coverage->second;
}
min = min->Min(coverage->first);
max = max->Max(coverage->second);
}
if (!min.has_value() || !max.has_value()) {
return {};
}
const auto diff = *max - *min;
return {min->x, min->y, diff.x, diff.y};
}
} // namespace impeller

View File

@ -5,6 +5,7 @@
#pragma once
#include <memory>
#include <optional>
#include "flutter/fml/macros.h"
#include "impeller/entity/contents.h"
@ -20,6 +21,8 @@ class CanvasPass {
void PushEntity(Entity entity);
Rect GetCoverageRect() const;
const std::vector<Entity>& GetPassEntities() const;
void SetPostProcessingEntity(Entity entity);
@ -31,4 +34,10 @@ class CanvasPass {
Entity post_processing_entity_;
};
struct CanvasStackEntry {
Matrix xformation;
size_t stencil_depth = 0u;
std::optional<CanvasPass> pass;
};
} // namespace impeller

View File

@ -4,8 +4,8 @@
#pragma once
#include <deque>
#include <memory>
#include <vector>
#include "flutter/fml/macros.h"
#include "impeller/aiks/canvas_pass.h"
@ -14,7 +14,7 @@
namespace impeller {
struct Picture {
std::vector<CanvasPass> passes;
std::deque<CanvasStackEntry> entries;
};
} // namespace impeller

View File

@ -29,10 +29,12 @@ bool PictureRenderer::Render(const Surface& surface,
return false;
}
for (const auto& pass : picture.passes) {
if (!entity_renderer_.RenderEntities(surface, parent_pass,
pass.GetPassEntities())) {
return false;
for (const auto& entry : picture.entries) {
if (auto pass = entry.pass) {
if (!entity_renderer_.RenderEntities(surface, parent_pass,
pass->GetPassEntities())) {
return false;
}
}
}

View File

@ -42,4 +42,8 @@ uint32_t Entity::GetStencilDepth() const {
return stencil_depth_;
}
void Entity::IncrementStencilDepth(uint32_t increment) {
stencil_depth_ += increment;
}
} // namespace impeller

View File

@ -33,6 +33,8 @@ class Entity {
void SetStencilDepth(uint32_t stencil_depth);
void IncrementStencilDepth(uint32_t increment);
uint32_t GetStencilDepth() const;
private:

View File

@ -62,6 +62,10 @@ struct Color {
static constexpr Color Blue() { return {0.0, 0.0, 1.0, 1.0}; }
constexpr Color WithAlpha(Scalar new_alpha) const {
return {red, green, blue, new_alpha};
}
static constexpr Color AliceBlue() {
return {240.0 / 255.0, 248.0 / 255.0, 255.0 / 255.0, 1.0};
}

View File

@ -174,9 +174,20 @@ std::vector<Point> Path::CreatePolyline(
}
Rect Path::GetBoundingBox() const {
if (linears_.empty() && quads_.empty() && cubics_.empty()) {
auto min_max = GetMinMaxCoveragePoints();
if (!min_max.has_value()) {
return {};
}
auto min = min_max->first;
auto max = min_max->second;
const auto difference = max - min;
return {min.x, min.y, difference.x, difference.y};
}
std::optional<std::pair<Point, Point>> Path::GetMinMaxCoveragePoints() const {
if (linears_.empty() && quads_.empty() && cubics_.empty()) {
return std::nullopt;
}
std::optional<Point> min, max;
@ -209,9 +220,7 @@ Rect Path::GetBoundingBox() const {
clamp(cubic.Extrema());
}
const auto difference = *max - *min;
return {min->x, min->y, difference.x, difference.y};
return std::make_pair(min.value(), max.value());
}
} // namespace impeller

View File

@ -5,6 +5,7 @@
#pragma once
#include <functional>
#include <optional>
#include <vector>
#include "impeller/geometry/path_component.h"
@ -68,6 +69,8 @@ class Path {
Rect GetBoundingBox() const;
std::optional<std::pair<Point, Point>> GetMinMaxCoveragePoints() const;
private:
struct ComponentIndexPair {
ComponentType type = ComponentType::kLinear;

View File

@ -4,6 +4,7 @@
#pragma once
#include <algorithm>
#include <cmath>
#include <string>
@ -83,6 +84,14 @@ struct TPoint {
return dx * dx + dy * dy;
}
constexpr TPoint Min(const TPoint& p) const {
return {std::min<Type>(x, p.x), std::min<Type>(y, p.y)};
}
constexpr TPoint Max(const TPoint& p) const {
return {std::max<Type>(x, p.x), std::max<Type>(y, p.y)};
}
constexpr Type GetDistance(const TPoint& p) const {
return sqrt(GetDistanceSquared(p));
}