| // 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. | |
| #include "flutter/flow/layers/layer_state_stack.h" | |
| #include "flutter/display_list/utils/dl_matrix_clip_tracker.h" | |
| #include "flutter/flow/layers/layer.h" | |
| #include "flutter/flow/paint_utils.h" | |
| #include "flutter/flow/raster_cache_util.h" | |
| namespace flutter { | |
| // ============================================================== | |
| // Delegate subclasses | |
| // ============================================================== | |
| // The DummyDelegate class implements most Delegate methods as NOP | |
| // but throws errors if the caller starts executing query methods | |
| // that require an active delegate to be tracking. It is specifically | |
| // designed to be immutable, lightweight, and a singleton so that it | |
| // can be substituted into the delegate slot in a LayerStateStack | |
| // quickly and cheaply when no externally supplied delegates are present. | |
| class DummyDelegate : public LayerStateStack::Delegate { | |
| public: | |
| static const std::shared_ptr<DummyDelegate> kInstance; | |
| void decommission() override {} | |
| SkRect local_cull_rect() const override { | |
| error(); | |
| return {}; | |
| } | |
| SkRect device_cull_rect() const override { | |
| error(); | |
| return {}; | |
| } | |
| SkM44 matrix_4x4() const override { | |
| error(); | |
| return {}; | |
| } | |
| SkMatrix matrix_3x3() const override { | |
| error(); | |
| return {}; | |
| } | |
| bool content_culled(const SkRect& content_bounds) const override { | |
| error(); | |
| return true; | |
| } | |
| void save() override {} | |
| void saveLayer(const SkRect& bounds, | |
| LayerStateStack::RenderingAttributes& attributes, | |
| DlBlendMode blend, | |
| const DlImageFilter* backdrop) override {} | |
| void restore() override {} | |
| void translate(SkScalar tx, SkScalar ty) override {} | |
| void transform(const SkM44& m44) override {} | |
| void transform(const SkMatrix& matrix) override {} | |
| void integralTransform() override {} | |
| void clipRect(const SkRect& rect, ClipOp op, bool is_aa) override {} | |
| void clipRRect(const SkRRect& rrect, ClipOp op, bool is_aa) override {} | |
| void clipPath(const SkPath& path, ClipOp op, bool is_aa) override {} | |
| private: | |
| static void error() { | |
| FML_DCHECK(false) << "LayerStateStack state queried without a delegate"; | |
| } | |
| }; | |
| const std::shared_ptr<DummyDelegate> DummyDelegate::kInstance = | |
| std::make_shared<DummyDelegate>(); | |
| class DlCanvasDelegate : public LayerStateStack::Delegate { | |
| public: | |
| explicit DlCanvasDelegate(DlCanvas* canvas) | |
| : canvas_(canvas), initial_save_level_(canvas->GetSaveCount()) {} | |
| void decommission() override { canvas_->RestoreToCount(initial_save_level_); } | |
| DlCanvas* canvas() const override { return canvas_; } | |
| SkRect local_cull_rect() const override { | |
| return canvas_->GetLocalClipBounds(); | |
| } | |
| SkRect device_cull_rect() const override { | |
| return canvas_->GetDestinationClipBounds(); | |
| } | |
| SkM44 matrix_4x4() const override { | |
| return canvas_->GetTransformFullPerspective(); | |
| } | |
| SkMatrix matrix_3x3() const override { return canvas_->GetTransform(); } | |
| bool content_culled(const SkRect& content_bounds) const override { | |
| return canvas_->QuickReject(content_bounds); | |
| } | |
| void save() override { canvas_->Save(); } | |
| void saveLayer(const SkRect& bounds, | |
| LayerStateStack::RenderingAttributes& attributes, | |
| DlBlendMode blend_mode, | |
| const DlImageFilter* backdrop) override { | |
| TRACE_EVENT0("flutter", "Canvas::saveLayer"); | |
| DlPaint paint; | |
| canvas_->SaveLayer(&bounds, attributes.fill(paint, blend_mode), backdrop); | |
| } | |
| void restore() override { canvas_->Restore(); } | |
| void translate(SkScalar tx, SkScalar ty) override { | |
| canvas_->Translate(tx, ty); | |
| } | |
| void transform(const SkM44& m44) override { canvas_->Transform(m44); } | |
| void transform(const SkMatrix& matrix) override { | |
| canvas_->Transform(matrix); | |
| } | |
| void integralTransform() override { | |
| SkM44 integral; | |
| if (RasterCacheUtil::ComputeIntegralTransCTM(matrix_4x4(), &integral)) { | |
| canvas_->SetTransform(integral); | |
| } | |
| } | |
| void clipRect(const SkRect& rect, ClipOp op, bool is_aa) override { | |
| canvas_->ClipRect(rect, op, is_aa); | |
| } | |
| void clipRRect(const SkRRect& rrect, ClipOp op, bool is_aa) override { | |
| canvas_->ClipRRect(rrect, op, is_aa); | |
| } | |
| void clipPath(const SkPath& path, ClipOp op, bool is_aa) override { | |
| canvas_->ClipPath(path, op, is_aa); | |
| } | |
| private: | |
| DlCanvas* canvas_; | |
| const int initial_save_level_; | |
| }; | |
| class PrerollDelegate : public LayerStateStack::Delegate { | |
| public: | |
| PrerollDelegate(const SkRect& cull_rect, const SkMatrix& matrix) | |
| : tracker_(cull_rect, matrix) {} | |
| void decommission() override {} | |
| SkM44 matrix_4x4() const override { return tracker_.matrix_4x4(); } | |
| SkMatrix matrix_3x3() const override { return tracker_.matrix_3x3(); } | |
| SkRect local_cull_rect() const override { return tracker_.local_cull_rect(); } | |
| SkRect device_cull_rect() const override { | |
| return tracker_.device_cull_rect(); | |
| } | |
| bool content_culled(const SkRect& content_bounds) const override { | |
| return tracker_.content_culled(content_bounds); | |
| } | |
| void save() override { tracker_.save(); } | |
| void saveLayer(const SkRect& bounds, | |
| LayerStateStack::RenderingAttributes& attributes, | |
| DlBlendMode blend, | |
| const DlImageFilter* backdrop) override { | |
| tracker_.save(); | |
| } | |
| void restore() override { tracker_.restore(); } | |
| void translate(SkScalar tx, SkScalar ty) override { | |
| tracker_.translate(tx, ty); | |
| } | |
| void transform(const SkM44& m44) override { tracker_.transform(m44); } | |
| void transform(const SkMatrix& matrix) override { | |
| tracker_.transform(matrix); | |
| } | |
| void integralTransform() override { | |
| if (tracker_.using_4x4_matrix()) { | |
| SkM44 integral; | |
| if (RasterCacheUtil::ComputeIntegralTransCTM(tracker_.matrix_4x4(), | |
| &integral)) { | |
| tracker_.setTransform(integral); | |
| } | |
| } else { | |
| SkMatrix integral; | |
| if (RasterCacheUtil::ComputeIntegralTransCTM(tracker_.matrix_3x3(), | |
| &integral)) { | |
| tracker_.setTransform(integral); | |
| } | |
| } | |
| } | |
| void clipRect(const SkRect& rect, ClipOp op, bool is_aa) override { | |
| tracker_.clipRect(rect, op, is_aa); | |
| } | |
| void clipRRect(const SkRRect& rrect, ClipOp op, bool is_aa) override { | |
| tracker_.clipRRect(rrect, op, is_aa); | |
| } | |
| void clipPath(const SkPath& path, ClipOp op, bool is_aa) override { | |
| tracker_.clipPath(path, op, is_aa); | |
| } | |
| private: | |
| DisplayListMatrixClipTracker tracker_; | |
| }; | |
| // ============================================================== | |
| // StateEntry subclasses | |
| // ============================================================== | |
| class SaveEntry : public LayerStateStack::StateEntry { | |
| public: | |
| SaveEntry() = default; | |
| void apply(LayerStateStack* stack) const override { | |
| stack->delegate_->save(); | |
| } | |
| void restore(LayerStateStack* stack) const override { | |
| stack->delegate_->restore(); | |
| } | |
| FML_DISALLOW_COPY_ASSIGN_AND_MOVE(SaveEntry); | |
| }; | |
| class SaveLayerEntry : public LayerStateStack::StateEntry { | |
| public: | |
| SaveLayerEntry(const SkRect& bounds, | |
| DlBlendMode blend_mode, | |
| const LayerStateStack::RenderingAttributes& prev) | |
| : bounds_(bounds), blend_mode_(blend_mode), old_attributes_(prev) {} | |
| void apply(LayerStateStack* stack) const override { | |
| stack->delegate_->saveLayer(bounds_, stack->outstanding_, blend_mode_, | |
| nullptr); | |
| stack->outstanding_ = {}; | |
| } | |
| void restore(LayerStateStack* stack) const override { | |
| if (stack->checkerboard_func_) { | |
| DlCanvas* canvas = stack->canvas_delegate(); | |
| if (canvas != nullptr) { | |
| (*stack->checkerboard_func_)(canvas, bounds_); | |
| } | |
| } | |
| stack->delegate_->restore(); | |
| stack->outstanding_ = old_attributes_; | |
| } | |
| protected: | |
| const SkRect bounds_; | |
| const DlBlendMode blend_mode_; | |
| const LayerStateStack::RenderingAttributes old_attributes_; | |
| FML_DISALLOW_COPY_ASSIGN_AND_MOVE(SaveLayerEntry); | |
| }; | |
| class OpacityEntry : public LayerStateStack::StateEntry { | |
| public: | |
| OpacityEntry(const SkRect& bounds, | |
| SkScalar opacity, | |
| const LayerStateStack::RenderingAttributes& prev) | |
| : bounds_(bounds), | |
| opacity_(opacity), | |
| old_opacity_(prev.opacity), | |
| old_bounds_(prev.save_layer_bounds) {} | |
| void apply(LayerStateStack* stack) const override { | |
| stack->outstanding_.save_layer_bounds = bounds_; | |
| stack->outstanding_.opacity *= opacity_; | |
| } | |
| void restore(LayerStateStack* stack) const override { | |
| stack->outstanding_.save_layer_bounds = old_bounds_; | |
| stack->outstanding_.opacity = old_opacity_; | |
| } | |
| void update_mutators(MutatorsStack* mutators_stack) const override { | |
| mutators_stack->PushOpacity(DlColor::toAlpha(opacity_)); | |
| } | |
| private: | |
| const SkRect bounds_; | |
| const SkScalar opacity_; | |
| const SkScalar old_opacity_; | |
| const SkRect old_bounds_; | |
| FML_DISALLOW_COPY_ASSIGN_AND_MOVE(OpacityEntry); | |
| }; | |
| class ImageFilterEntry : public LayerStateStack::StateEntry { | |
| public: | |
| ImageFilterEntry(const SkRect& bounds, | |
| const std::shared_ptr<const DlImageFilter>& filter, | |
| const LayerStateStack::RenderingAttributes& prev) | |
| : bounds_(bounds), | |
| filter_(filter), | |
| old_filter_(prev.image_filter), | |
| old_bounds_(prev.save_layer_bounds) {} | |
| ~ImageFilterEntry() override = default; | |
| void apply(LayerStateStack* stack) const override { | |
| stack->outstanding_.save_layer_bounds = bounds_; | |
| stack->outstanding_.image_filter = filter_; | |
| } | |
| void restore(LayerStateStack* stack) const override { | |
| stack->outstanding_.save_layer_bounds = old_bounds_; | |
| stack->outstanding_.image_filter = old_filter_; | |
| } | |
| // There is no ImageFilter mutator currently | |
| // void update_mutators(MutatorsStack* mutators_stack) const override; | |
| private: | |
| const SkRect bounds_; | |
| const std::shared_ptr<const DlImageFilter> filter_; | |
| const std::shared_ptr<const DlImageFilter> old_filter_; | |
| const SkRect old_bounds_; | |
| FML_DISALLOW_COPY_ASSIGN_AND_MOVE(ImageFilterEntry); | |
| }; | |
| class ColorFilterEntry : public LayerStateStack::StateEntry { | |
| public: | |
| ColorFilterEntry(const SkRect& bounds, | |
| const std::shared_ptr<const DlColorFilter>& filter, | |
| const LayerStateStack::RenderingAttributes& prev) | |
| : bounds_(bounds), | |
| filter_(filter), | |
| old_filter_(prev.color_filter), | |
| old_bounds_(prev.save_layer_bounds) {} | |
| ~ColorFilterEntry() override = default; | |
| void apply(LayerStateStack* stack) const override { | |
| stack->outstanding_.save_layer_bounds = bounds_; | |
| stack->outstanding_.color_filter = filter_; | |
| } | |
| void restore(LayerStateStack* stack) const override { | |
| stack->outstanding_.save_layer_bounds = old_bounds_; | |
| stack->outstanding_.color_filter = old_filter_; | |
| } | |
| // There is no ColorFilter mutator currently | |
| // void update_mutators(MutatorsStack* mutators_stack) const override; | |
| private: | |
| const SkRect bounds_; | |
| const std::shared_ptr<const DlColorFilter> filter_; | |
| const std::shared_ptr<const DlColorFilter> old_filter_; | |
| const SkRect old_bounds_; | |
| FML_DISALLOW_COPY_ASSIGN_AND_MOVE(ColorFilterEntry); | |
| }; | |
| class BackdropFilterEntry : public SaveLayerEntry { | |
| public: | |
| BackdropFilterEntry(const SkRect& bounds, | |
| const std::shared_ptr<const DlImageFilter>& filter, | |
| DlBlendMode blend_mode, | |
| const LayerStateStack::RenderingAttributes& prev) | |
| : SaveLayerEntry(bounds, blend_mode, prev), filter_(filter) {} | |
| ~BackdropFilterEntry() override = default; | |
| void apply(LayerStateStack* stack) const override { | |
| stack->delegate_->saveLayer(bounds_, stack->outstanding_, blend_mode_, | |
| filter_.get()); | |
| stack->outstanding_ = {}; | |
| } | |
| void reapply(LayerStateStack* stack) const override { | |
| // On the reapply for subsequent overlay layers, we do not | |
| // want to reapply the backdrop filter, but we do need to | |
| // do a saveLayer to encapsulate the contents and match the | |
| // restore that will be forthcoming. Note that this is not | |
| // perfect if the BlendMode is not associative as we will be | |
| // compositing multiple parts of the content in batches. | |
| // Luckily the most common SrcOver is associative. | |
| SaveLayerEntry::apply(stack); | |
| } | |
| private: | |
| const std::shared_ptr<const DlImageFilter> filter_; | |
| FML_DISALLOW_COPY_ASSIGN_AND_MOVE(BackdropFilterEntry); | |
| }; | |
| class TranslateEntry : public LayerStateStack::StateEntry { | |
| public: | |
| TranslateEntry(SkScalar tx, SkScalar ty) : tx_(tx), ty_(ty) {} | |
| void apply(LayerStateStack* stack) const override { | |
| stack->delegate_->translate(tx_, ty_); | |
| } | |
| void update_mutators(MutatorsStack* mutators_stack) const override { | |
| mutators_stack->PushTransform(SkMatrix::Translate(tx_, ty_)); | |
| } | |
| private: | |
| const SkScalar tx_; | |
| const SkScalar ty_; | |
| FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TranslateEntry); | |
| }; | |
| class TransformMatrixEntry : public LayerStateStack::StateEntry { | |
| public: | |
| explicit TransformMatrixEntry(const SkMatrix& matrix) : matrix_(matrix) {} | |
| void apply(LayerStateStack* stack) const override { | |
| stack->delegate_->transform(matrix_); | |
| } | |
| void update_mutators(MutatorsStack* mutators_stack) const override { | |
| mutators_stack->PushTransform(matrix_); | |
| } | |
| private: | |
| const SkMatrix matrix_; | |
| FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TransformMatrixEntry); | |
| }; | |
| class TransformM44Entry : public LayerStateStack::StateEntry { | |
| public: | |
| explicit TransformM44Entry(const SkM44& m44) : m44_(m44) {} | |
| void apply(LayerStateStack* stack) const override { | |
| stack->delegate_->transform(m44_); | |
| } | |
| void update_mutators(MutatorsStack* mutators_stack) const override { | |
| mutators_stack->PushTransform(m44_.asM33()); | |
| } | |
| private: | |
| const SkM44 m44_; | |
| FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TransformM44Entry); | |
| }; | |
| class IntegralTransformEntry : public LayerStateStack::StateEntry { | |
| public: | |
| IntegralTransformEntry() = default; | |
| void apply(LayerStateStack* stack) const override { | |
| stack->delegate_->integralTransform(); | |
| } | |
| private: | |
| FML_DISALLOW_COPY_ASSIGN_AND_MOVE(IntegralTransformEntry); | |
| }; | |
| class ClipRectEntry : public LayerStateStack::StateEntry { | |
| public: | |
| ClipRectEntry(const SkRect& clip_rect, bool is_aa) | |
| : clip_rect_(clip_rect), is_aa_(is_aa) {} | |
| void apply(LayerStateStack* stack) const override { | |
| stack->delegate_->clipRect(clip_rect_, DlCanvas::ClipOp::kIntersect, | |
| is_aa_); | |
| } | |
| void update_mutators(MutatorsStack* mutators_stack) const override { | |
| mutators_stack->PushClipRect(clip_rect_); | |
| } | |
| private: | |
| const SkRect clip_rect_; | |
| const bool is_aa_; | |
| FML_DISALLOW_COPY_ASSIGN_AND_MOVE(ClipRectEntry); | |
| }; | |
| class ClipRRectEntry : public LayerStateStack::StateEntry { | |
| public: | |
| ClipRRectEntry(const SkRRect& clip_rrect, bool is_aa) | |
| : clip_rrect_(clip_rrect), is_aa_(is_aa) {} | |
| void apply(LayerStateStack* stack) const override { | |
| stack->delegate_->clipRRect(clip_rrect_, DlCanvas::ClipOp::kIntersect, | |
| is_aa_); | |
| } | |
| void update_mutators(MutatorsStack* mutators_stack) const override { | |
| mutators_stack->PushClipRRect(clip_rrect_); | |
| } | |
| private: | |
| const SkRRect clip_rrect_; | |
| const bool is_aa_; | |
| FML_DISALLOW_COPY_ASSIGN_AND_MOVE(ClipRRectEntry); | |
| }; | |
| class ClipPathEntry : public LayerStateStack::StateEntry { | |
| public: | |
| ClipPathEntry(const SkPath& clip_path, bool is_aa) | |
| : clip_path_(clip_path), is_aa_(is_aa) {} | |
| ~ClipPathEntry() override = default; | |
| void apply(LayerStateStack* stack) const override { | |
| stack->delegate_->clipPath(clip_path_, DlCanvas::ClipOp::kIntersect, | |
| is_aa_); | |
| } | |
| void update_mutators(MutatorsStack* mutators_stack) const override { | |
| mutators_stack->PushClipPath(clip_path_); | |
| } | |
| private: | |
| const SkPath clip_path_; | |
| const bool is_aa_; | |
| FML_DISALLOW_COPY_ASSIGN_AND_MOVE(ClipPathEntry); | |
| }; | |
| // ============================================================== | |
| // RenderingAttributes methods | |
| // ============================================================== | |
| DlPaint* LayerStateStack::RenderingAttributes::fill(DlPaint& paint, | |
| DlBlendMode mode) const { | |
| DlPaint* ret = nullptr; | |
| if (opacity < SK_Scalar1) { | |
| paint.setOpacity(std::max(opacity, 0.0f)); | |
| ret = &paint; | |
| } else { | |
| paint.setOpacity(SK_Scalar1); | |
| } | |
| paint.setColorFilter(color_filter); | |
| if (color_filter) { | |
| ret = &paint; | |
| } | |
| paint.setImageFilter(image_filter); | |
| if (image_filter) { | |
| ret = &paint; | |
| } | |
| paint.setBlendMode(mode); | |
| if (mode != DlBlendMode::kSrcOver) { | |
| ret = &paint; | |
| } | |
| return ret; | |
| } | |
| // ============================================================== | |
| // MutatorContext methods | |
| // ============================================================== | |
| using MutatorContext = LayerStateStack::MutatorContext; | |
| void MutatorContext::saveLayer(const SkRect& bounds) { | |
| layer_state_stack_->save_layer(bounds); | |
| } | |
| void MutatorContext::applyOpacity(const SkRect& bounds, SkScalar opacity) { | |
| if (opacity < SK_Scalar1) { | |
| layer_state_stack_->push_opacity(bounds, opacity); | |
| } | |
| } | |
| void MutatorContext::applyImageFilter( | |
| const SkRect& bounds, | |
| const std::shared_ptr<const DlImageFilter>& filter) { | |
| if (filter) { | |
| layer_state_stack_->push_image_filter(bounds, filter); | |
| } | |
| } | |
| void MutatorContext::applyColorFilter( | |
| const SkRect& bounds, | |
| const std::shared_ptr<const DlColorFilter>& filter) { | |
| if (filter) { | |
| layer_state_stack_->push_color_filter(bounds, filter); | |
| } | |
| } | |
| void MutatorContext::applyBackdropFilter( | |
| const SkRect& bounds, | |
| const std::shared_ptr<const DlImageFilter>& filter, | |
| DlBlendMode blend_mode) { | |
| layer_state_stack_->push_backdrop(bounds, filter, blend_mode); | |
| } | |
| void MutatorContext::translate(SkScalar tx, SkScalar ty) { | |
| if (!(tx == 0 && ty == 0)) { | |
| layer_state_stack_->maybe_save_layer_for_transform(save_needed_); | |
| save_needed_ = false; | |
| layer_state_stack_->push_translate(tx, ty); | |
| } | |
| } | |
| void MutatorContext::transform(const SkMatrix& matrix) { | |
| if (matrix.isTranslate()) { | |
| translate(matrix.getTranslateX(), matrix.getTranslateY()); | |
| } else if (!matrix.isIdentity()) { | |
| layer_state_stack_->maybe_save_layer_for_transform(save_needed_); | |
| save_needed_ = false; | |
| layer_state_stack_->push_transform(matrix); | |
| } | |
| } | |
| void MutatorContext::transform(const SkM44& m44) { | |
| if (DisplayListMatrixClipTracker::is_3x3(m44)) { | |
| transform(m44.asM33()); | |
| } else { | |
| layer_state_stack_->maybe_save_layer_for_transform(save_needed_); | |
| save_needed_ = false; | |
| layer_state_stack_->push_transform(m44); | |
| } | |
| } | |
| void MutatorContext::integralTransform() { | |
| layer_state_stack_->maybe_save_layer_for_transform(save_needed_); | |
| save_needed_ = false; | |
| layer_state_stack_->push_integral_transform(); | |
| } | |
| void MutatorContext::clipRect(const SkRect& rect, bool is_aa) { | |
| layer_state_stack_->maybe_save_layer_for_clip(save_needed_); | |
| save_needed_ = false; | |
| layer_state_stack_->push_clip_rect(rect, is_aa); | |
| } | |
| void MutatorContext::clipRRect(const SkRRect& rrect, bool is_aa) { | |
| layer_state_stack_->maybe_save_layer_for_clip(save_needed_); | |
| save_needed_ = false; | |
| layer_state_stack_->push_clip_rrect(rrect, is_aa); | |
| } | |
| void MutatorContext::clipPath(const SkPath& path, bool is_aa) { | |
| layer_state_stack_->maybe_save_layer_for_clip(save_needed_); | |
| save_needed_ = false; | |
| layer_state_stack_->push_clip_path(path, is_aa); | |
| } | |
| // ============================================================== | |
| // LayerStateStack methods | |
| // ============================================================== | |
| LayerStateStack::LayerStateStack() : delegate_(DummyDelegate::kInstance) {} | |
| void LayerStateStack::clear_delegate() { | |
| delegate_->decommission(); | |
| delegate_ = DummyDelegate::kInstance; | |
| } | |
| void LayerStateStack::set_delegate(DlCanvas* canvas) { | |
| if (delegate_) { | |
| if (canvas == delegate_->canvas()) { | |
| return; | |
| } | |
| clear_delegate(); | |
| } | |
| if (canvas) { | |
| delegate_ = std::make_shared<DlCanvasDelegate>(canvas); | |
| reapply_all(); | |
| } | |
| } | |
| void LayerStateStack::set_preroll_delegate(const SkRect& cull_rect) { | |
| set_preroll_delegate(cull_rect, SkMatrix::I()); | |
| } | |
| void LayerStateStack::set_preroll_delegate(const SkMatrix& matrix) { | |
| set_preroll_delegate(kGiantRect, matrix); | |
| } | |
| void LayerStateStack::set_preroll_delegate(const SkRect& cull_rect, | |
| const SkMatrix& matrix) { | |
| clear_delegate(); | |
| delegate_ = std::make_shared<PrerollDelegate>(cull_rect, matrix); | |
| reapply_all(); | |
| } | |
| void LayerStateStack::reapply_all() { | |
| // We use a local RenderingAttributes instance so that it can track the | |
| // necessary state changes independently as they occur in the stack. | |
| // Reusing |outstanding_| would wreak havoc on the current state of | |
| // the stack. When we are finished, though, the local attributes | |
| // contents should match the current outstanding_ values; | |
| RenderingAttributes attributes = outstanding_; | |
| outstanding_ = {}; | |
| for (auto& state : state_stack_) { | |
| state->reapply(this); | |
| } | |
| FML_DCHECK(attributes == outstanding_); | |
| } | |
| void LayerStateStack::fill(MutatorsStack* mutators) { | |
| for (auto& state : state_stack_) { | |
| state->update_mutators(mutators); | |
| } | |
| } | |
| void LayerStateStack::restore_to_count(size_t restore_count) { | |
| while (state_stack_.size() > restore_count) { | |
| state_stack_.back()->restore(this); | |
| state_stack_.pop_back(); | |
| } | |
| } | |
| void LayerStateStack::push_opacity(const SkRect& bounds, SkScalar opacity) { | |
| maybe_save_layer(opacity); | |
| state_stack_.emplace_back( | |
| std::make_unique<OpacityEntry>(bounds, opacity, outstanding_)); | |
| apply_last_entry(); | |
| } | |
| void LayerStateStack::push_color_filter( | |
| const SkRect& bounds, | |
| const std::shared_ptr<const DlColorFilter>& filter) { | |
| maybe_save_layer(filter); | |
| state_stack_.emplace_back( | |
| std::make_unique<ColorFilterEntry>(bounds, filter, outstanding_)); | |
| apply_last_entry(); | |
| } | |
| void LayerStateStack::push_image_filter( | |
| const SkRect& bounds, | |
| const std::shared_ptr<const DlImageFilter>& filter) { | |
| maybe_save_layer(filter); | |
| state_stack_.emplace_back( | |
| std::make_unique<ImageFilterEntry>(bounds, filter, outstanding_)); | |
| apply_last_entry(); | |
| } | |
| void LayerStateStack::push_backdrop( | |
| const SkRect& bounds, | |
| const std::shared_ptr<const DlImageFilter>& filter, | |
| DlBlendMode blend_mode) { | |
| state_stack_.emplace_back(std::make_unique<BackdropFilterEntry>( | |
| bounds, filter, blend_mode, outstanding_)); | |
| apply_last_entry(); | |
| } | |
| void LayerStateStack::push_translate(SkScalar tx, SkScalar ty) { | |
| state_stack_.emplace_back(std::make_unique<TranslateEntry>(tx, ty)); | |
| apply_last_entry(); | |
| } | |
| void LayerStateStack::push_transform(const SkM44& m44) { | |
| state_stack_.emplace_back(std::make_unique<TransformM44Entry>(m44)); | |
| apply_last_entry(); | |
| } | |
| void LayerStateStack::push_transform(const SkMatrix& matrix) { | |
| state_stack_.emplace_back(std::make_unique<TransformMatrixEntry>(matrix)); | |
| apply_last_entry(); | |
| } | |
| void LayerStateStack::push_integral_transform() { | |
| state_stack_.emplace_back(std::make_unique<IntegralTransformEntry>()); | |
| apply_last_entry(); | |
| } | |
| void LayerStateStack::push_clip_rect(const SkRect& rect, bool is_aa) { | |
| state_stack_.emplace_back(std::make_unique<ClipRectEntry>(rect, is_aa)); | |
| apply_last_entry(); | |
| } | |
| void LayerStateStack::push_clip_rrect(const SkRRect& rrect, bool is_aa) { | |
| state_stack_.emplace_back(std::make_unique<ClipRRectEntry>(rrect, is_aa)); | |
| apply_last_entry(); | |
| } | |
| void LayerStateStack::push_clip_path(const SkPath& path, bool is_aa) { | |
| state_stack_.emplace_back(std::make_unique<ClipPathEntry>(path, is_aa)); | |
| apply_last_entry(); | |
| } | |
| bool LayerStateStack::needs_save_layer(int flags) const { | |
| if (outstanding_.opacity < SK_Scalar1 && | |
| (flags & LayerStateStack::kCallerCanApplyOpacity) == 0) { | |
| return true; | |
| } | |
| if (outstanding_.image_filter && | |
| (flags & LayerStateStack::kCallerCanApplyImageFilter) == 0) { | |
| return true; | |
| } | |
| if (outstanding_.color_filter && | |
| (flags & LayerStateStack::kCallerCanApplyColorFilter) == 0) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| void LayerStateStack::do_save() { | |
| state_stack_.emplace_back(std::make_unique<SaveEntry>()); | |
| apply_last_entry(); | |
| } | |
| void LayerStateStack::save_layer(const SkRect& bounds) { | |
| state_stack_.emplace_back(std::make_unique<SaveLayerEntry>( | |
| bounds, DlBlendMode::kSrcOver, outstanding_)); | |
| apply_last_entry(); | |
| } | |
| void LayerStateStack::maybe_save_layer_for_transform(bool save_needed) { | |
| // Alpha and ColorFilter don't care about transform | |
| if (outstanding_.image_filter) { | |
| save_layer(outstanding_.save_layer_bounds); | |
| } else if (save_needed) { | |
| do_save(); | |
| } | |
| } | |
| void LayerStateStack::maybe_save_layer_for_clip(bool save_needed) { | |
| // Alpha and ColorFilter don't care about clipping | |
| // - Alpha of clipped content == clip of alpha content | |
| // - Color-filtering of clipped content == clip of color-filtered content | |
| if (outstanding_.image_filter) { | |
| save_layer(outstanding_.save_layer_bounds); | |
| } else if (save_needed) { | |
| do_save(); | |
| } | |
| } | |
| void LayerStateStack::maybe_save_layer(int apply_flags) { | |
| if (needs_save_layer(apply_flags)) { | |
| save_layer(outstanding_.save_layer_bounds); | |
| } | |
| } | |
| void LayerStateStack::maybe_save_layer(SkScalar opacity) { | |
| if (outstanding_.image_filter) { | |
| save_layer(outstanding_.save_layer_bounds); | |
| } | |
| } | |
| void LayerStateStack::maybe_save_layer( | |
| const std::shared_ptr<const DlColorFilter>& filter) { | |
| if (outstanding_.color_filter || outstanding_.image_filter || | |
| (outstanding_.opacity < SK_Scalar1 && | |
| !filter->can_commute_with_opacity())) { | |
| // TBD: compose the 2 color filters together. | |
| save_layer(outstanding_.save_layer_bounds); | |
| } | |
| } | |
| void LayerStateStack::maybe_save_layer( | |
| const std::shared_ptr<const DlImageFilter>& filter) { | |
| if (outstanding_.image_filter) { | |
| // TBD: compose the 2 image filters together. | |
| save_layer(outstanding_.save_layer_bounds); | |
| } | |
| } | |
| } // namespace flutter |