blob: 3c5b9321004447af5c1ca3bca7e4a91edab0dc48 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_IMPELLER_ENTITY_CONTENTS_COLOR_SOURCE_CONTENTS_H_
#define FLUTTER_IMPELLER_ENTITY_CONTENTS_COLOR_SOURCE_CONTENTS_H_
#include "fml/logging.h"
#include "impeller/entity/contents/clip_contents.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/contents/contents.h"
#include "impeller/entity/geometry/geometry.h"
#include "impeller/entity/geometry/rect_geometry.h"
#include "impeller/geometry/matrix.h"
namespace impeller {
//------------------------------------------------------------------------------
/// Color sources are geometry-ignostic `Contents` capable of shading any area
/// defined by an `impeller::Geometry`. Conceptually,
/// `impeller::ColorSourceContents` implement a particular color shading
/// behavior.
///
/// This separation of concerns between geometry and color source output allows
/// Impeller to handle most possible draw combinations in a consistent way.
/// For example: There are color sources for handling solid colors, gradients,
/// textures, custom runtime effects, and even 3D scenes.
///
/// There are some special rendering exceptions that deviate from this pattern
/// and cross geometry and color source concerns, such as text atlas and image
/// atlas rendering. Special `Contents` exist for rendering these behaviors
/// which don't implement `ColorSourceContents`.
///
/// @see `impeller::Geometry`
///
class ColorSourceContents : public Contents {
public:
ColorSourceContents();
~ColorSourceContents() override;
//----------------------------------------------------------------------------
/// @brief Set the geometry that this contents will use to render.
///
void SetGeometry(std::shared_ptr<Geometry> geometry);
//----------------------------------------------------------------------------
/// @brief Get the geometry that this contents will use to render.
///
const std::shared_ptr<Geometry>& GetGeometry() const;
//----------------------------------------------------------------------------
/// @brief Set the effect transform for this color source.
///
/// The effect transform is a transform matrix that is applied to
/// the shaded color output and does not impact geometry in any way.
///
/// For example: With repeat tiling, any gradient or
/// `TiledTextureContents` could be used with an effect transform to
/// inexpensively draw an infinite scrolling background pattern.
///
void SetEffectTransform(Matrix matrix);
//----------------------------------------------------------------------------
/// @brief Set the inverted effect transform for this color source.
///
/// When the effect transform is set via `SetEffectTransform`, the
/// value is inverted upon storage. The reason for this is that most
/// color sources internally use the inverted transform.
///
/// @return The inverse of the transform set by `SetEffectTransform`.
///
/// @see `SetEffectTransform`
///
const Matrix& GetInverseEffectTransform() const;
//----------------------------------------------------------------------------
/// @brief Set the opacity factor for this color source.
///
void SetOpacityFactor(Scalar opacity);
//----------------------------------------------------------------------------
/// @brief Get the opacity factor for this color source.
///
/// This value is is factored into the output of the color source in
/// addition to opacity information that may be supplied any other
/// inputs.
///
/// @note If set, the output of this method factors factors in the inherited
/// opacity of this `Contents`.
///
/// @see `Contents::CanInheritOpacity`
///
Scalar GetOpacityFactor() const;
virtual bool IsSolidColor() const;
// |Contents|
std::optional<Rect> GetCoverage(const Entity& entity) const override;
// |Contents|
bool CanInheritOpacity(const Entity& entity) const override;
// |Contents|
void SetInheritedOpacity(Scalar opacity) override;
protected:
using BindFragmentCallback = std::function<bool(RenderPass& pass)>;
using PipelineBuilderMethod = std::shared_ptr<Pipeline<PipelineDescriptor>> (
impeller::ContentContext::*)(ContentContextOptions) const;
using PipelineBuilderCallback =
std::function<std::shared_ptr<Pipeline<PipelineDescriptor>>(
ContentContextOptions)>;
template <typename VertexShaderT>
bool DrawGeometry(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const PipelineBuilderCallback& pipeline_callback,
typename VertexShaderT::FrameInfo frame_info,
const BindFragmentCallback& bind_fragment_callback,
bool enable_uvs = false,
Rect texture_coverage = {},
const Matrix& effect_transform = {}) const {
auto options = OptionsFromPassAndEntity(pass, entity);
GeometryResult::Mode geometry_mode = GetGeometry()->GetResultMode();
Geometry& geometry = *GetGeometry();
const bool is_stencil_then_cover =
geometry_mode == GeometryResult::Mode::kNonZero ||
geometry_mode == GeometryResult::Mode::kEvenOdd;
if (is_stencil_then_cover) {
pass.SetStencilReference(0);
/// Stencil preparation draw.
GeometryResult stencil_geometry_result =
GetGeometry()->GetPositionBuffer(renderer, entity, pass);
if (stencil_geometry_result.vertex_buffer.vertex_count == 0u) {
return true;
}
pass.SetVertexBuffer(std::move(stencil_geometry_result.vertex_buffer));
options.primitive_type = stencil_geometry_result.type;
options.blend_mode = BlendMode::kDestination;
switch (stencil_geometry_result.mode) {
case GeometryResult::Mode::kNonZero:
pass.SetCommandLabel("Stencil preparation (NonZero)");
options.stencil_mode =
ContentContextOptions::StencilMode::kStencilNonZeroFill;
break;
case GeometryResult::Mode::kEvenOdd:
pass.SetCommandLabel("Stencil preparation (EvenOdd)");
options.stencil_mode =
ContentContextOptions::StencilMode::kStencilEvenOddFill;
break;
default:
FML_UNREACHABLE();
}
pass.SetPipeline(renderer.GetClipPipeline(options));
ClipPipeline::VertexShader::FrameInfo clip_frame_info;
clip_frame_info.depth = entity.GetShaderClipDepth();
clip_frame_info.mvp = stencil_geometry_result.transform;
ClipPipeline::VertexShader::BindFrameInfo(
pass, renderer.GetTransientsBuffer().EmplaceUniform(clip_frame_info));
if (!pass.Draw().ok()) {
return false;
}
/// Cover draw.
options.blend_mode = entity.GetBlendMode();
options.stencil_mode = ContentContextOptions::StencilMode::kCoverCompare;
std::optional<Rect> maybe_cover_area = GetGeometry()->GetCoverage({});
if (!maybe_cover_area.has_value()) {
return true;
}
geometry = RectGeometry(maybe_cover_area.value());
}
GeometryResult geometry_result =
enable_uvs
? geometry.GetPositionUVBuffer(texture_coverage, effect_transform,
renderer, entity, pass)
: geometry.GetPositionBuffer(renderer, entity, pass);
if (geometry_result.vertex_buffer.vertex_count == 0u) {
return true;
}
pass.SetVertexBuffer(std::move(geometry_result.vertex_buffer));
options.primitive_type = geometry_result.type;
// Take the pre-populated vertex shader uniform struct and set managed
// values.
frame_info.mvp = geometry_result.transform;
// If overdraw prevention is enabled (like when drawing stroke paths), we
// increment the stencil buffer as we draw, preventing overlapping fragments
// from drawing. Afterwards, we need to append another draw call to clean up
// the stencil buffer (happens below in this method).
if (geometry_result.mode == GeometryResult::Mode::kPreventOverdraw) {
options.stencil_mode =
ContentContextOptions::StencilMode::kLegacyClipIncrement;
}
pass.SetStencilReference(0);
VertexShaderT::BindFrameInfo(
pass, renderer.GetTransientsBuffer().EmplaceUniform(frame_info));
// The reason we need to have a callback mechanism here is that this routine
// may insert draw calls before the main draw call below. For example, for
// sufficiently complex paths we may opt to use stencil-then-cover to avoid
// tessellation.
if (!bind_fragment_callback(pass)) {
return false;
}
pass.SetPipeline(pipeline_callback(options));
if (!pass.Draw().ok()) {
return false;
}
// If we performed overdraw prevention, a subsection of the clip heightmap
// was incremented by 1 in order to self-clip. So simply append a clip
// restore to clean it up.
if (geometry_result.mode == GeometryResult::Mode::kPreventOverdraw) {
auto restore = ClipRestoreContents();
restore.SetRestoreCoverage(GetCoverage(entity));
Entity restore_entity = entity.Clone();
return restore.Render(renderer, restore_entity, pass);
}
return true;
}
private:
std::shared_ptr<Geometry> geometry_;
Matrix inverse_matrix_;
Scalar opacity_ = 1.0;
Scalar inherited_opacity_ = 1.0;
ColorSourceContents(const ColorSourceContents&) = delete;
ColorSourceContents& operator=(const ColorSourceContents&) = delete;
};
} // namespace impeller
#endif // FLUTTER_IMPELLER_ENTITY_CONTENTS_COLOR_SOURCE_CONTENTS_H_