| // 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_DISPLAY_LIST_UTILS_DL_MATRIX_CLIP_TRACKER_H_ |
| #define FLUTTER_DISPLAY_LIST_UTILS_DL_MATRIX_CLIP_TRACKER_H_ |
| |
| #include <vector> |
| |
| #include "flutter/display_list/dl_canvas.h" |
| #include "flutter/display_list/geometry/dl_geometry_types.h" |
| #include "flutter/fml/logging.h" |
| |
| #include "third_party/skia/include/core/SkM44.h" |
| #include "third_party/skia/include/core/SkMatrix.h" |
| #include "third_party/skia/include/core/SkPath.h" |
| #include "third_party/skia/include/core/SkRRect.h" |
| #include "third_party/skia/include/core/SkRect.h" |
| #include "third_party/skia/include/core/SkScalar.h" |
| |
| namespace flutter { |
| |
| class DisplayListMatrixClipState { |
| private: |
| using ClipOp = DlCanvas::ClipOp; |
| |
| public: |
| explicit DisplayListMatrixClipState(const DlRect& cull_rect, |
| const DlMatrix& matrix = DlMatrix()); |
| explicit DisplayListMatrixClipState(const SkRect& cull_rect); |
| DisplayListMatrixClipState(const SkRect& cull_rect, const SkMatrix& matrix); |
| DisplayListMatrixClipState(const SkRect& cull_rect, const SkM44& matrix); |
| DisplayListMatrixClipState(const DisplayListMatrixClipState& other) = default; |
| |
| // This method should almost never be used as it breaks the encapsulation |
| // of the enclosing clips. However it is needed for practical purposes in |
| // some rare cases - such as when a saveLayer is collecting rendering |
| // operations prior to applying a filter on the entire layer bounds and |
| // some of those operations fall outside the enclosing clip, but their |
| // filtered content will spread out from where they were rendered on the |
| // layer into the enclosing clipped area. |
| // Omitting the |cull_rect| argument, or passing nullptr, will restore the |
| // cull rect to the initial value it had when the tracker was constructed. |
| void resetDeviceCullRect(const DlRect& cull_rect); |
| void resetDeviceCullRect(const SkRect& cull_rect) { |
| resetDeviceCullRect(ToDlRect(cull_rect)); |
| } |
| void resetLocalCullRect(const DlRect& cull_rect); |
| void resetLocalCullRect(const SkRect& cull_rect) { |
| resetLocalCullRect(ToDlRect(cull_rect)); |
| } |
| |
| bool using_4x4_matrix() const { return !matrix_.IsAffine(); } |
| bool is_matrix_invertable() const { return matrix_.GetDeterminant() != 0.0f; } |
| bool has_perspective() const { return matrix_.HasPerspective(); } |
| |
| const DlMatrix& matrix() const { return matrix_; } |
| SkM44 matrix_4x4() const { return SkM44::ColMajor(matrix_.m); } |
| SkMatrix matrix_3x3() const { return ToSkMatrix(matrix_); } |
| |
| SkRect local_cull_rect() const; |
| SkRect device_cull_rect() const { return ToSkRect(cull_rect_); } |
| |
| bool content_culled(const DlRect& content_bounds) const; |
| bool content_culled(const SkRect& content_bounds) const { |
| return content_culled(ToDlRect(content_bounds)); |
| } |
| bool is_cull_rect_empty() const { return cull_rect_.IsEmpty(); } |
| |
| void translate(SkScalar tx, SkScalar ty) { |
| matrix_ = matrix_.Translate({tx, ty}); |
| } |
| void scale(SkScalar sx, SkScalar sy) { |
| matrix_ = matrix_.Scale({sx, sy, 1.0f}); |
| } |
| void skew(SkScalar skx, SkScalar sky) { |
| matrix_ = matrix_ * DlMatrix::MakeSkew(skx, sky); |
| } |
| void rotate(SkScalar degrees) { |
| matrix_ = matrix_ * DlMatrix::MakeRotationZ(DlDegrees(degrees)); |
| } |
| void transform(const DlMatrix& matrix) { matrix_ = matrix_ * matrix; } |
| void transform(const SkM44& m44) { transform(ToDlMatrix(m44)); } |
| void transform(const SkMatrix& matrix) { transform(ToDlMatrix(matrix)); } |
| // clang-format off |
| void transform2DAffine( |
| SkScalar mxx, SkScalar mxy, SkScalar mxt, |
| SkScalar myx, SkScalar myy, SkScalar myt) { |
| matrix_ = matrix_ * DlMatrix::MakeColumn( |
| mxx, myx, 0.0f, 0.0f, |
| mxy, myy, 0.0f, 0.0f, |
| 0.0f, 0.0f, 1.0f, 0.0f, |
| mxt, myt, 0.0f, 1.0f |
| ); |
| } |
| void transformFullPerspective( |
| SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, |
| SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, |
| SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, |
| SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) { |
| matrix_ = matrix_ * DlMatrix::MakeColumn( |
| mxx, myx, mzx, mwx, |
| mxy, myy, mzy, mwy, |
| mxz, myz, mzz, mwz, |
| mxt, myt, mzt, mwt |
| ); |
| } |
| // clang-format on |
| void setTransform(const DlMatrix& matrix) { matrix_ = matrix; } |
| void setTransform(const SkMatrix& matrix) { matrix_ = ToDlMatrix(matrix); } |
| void setTransform(const SkM44& m44) { matrix_ = ToDlMatrix(m44); } |
| void setIdentity() { matrix_ = DlMatrix(); } |
| // If the matrix in |other_tracker| is invertible then transform this |
| // tracker by the inverse of its matrix and return true. Otherwise, |
| // return false and leave this tracker unmodified. |
| bool inverseTransform(const DisplayListMatrixClipState& other_tracker); |
| |
| bool mapRect(DlRect* rect) const { return mapRect(*rect, rect); } |
| bool mapRect(const DlRect& src, DlRect* mapped) const { |
| *mapped = src.TransformAndClipBounds(matrix_); |
| return matrix_.IsAligned2D(); |
| } |
| bool mapRect(SkRect* rect) const { return mapRect(*rect, rect); } |
| bool mapRect(const SkRect& src, SkRect* mapped) const { |
| *mapped = ToSkRect(ToDlRect(src).TransformAndClipBounds(matrix_)); |
| return matrix_.IsAligned2D(); |
| } |
| |
| /// @brief Maps the rect by the current matrix and then clips it against |
| /// the current cull rect, returning true if the result is non-empty. |
| bool mapAndClipRect(SkRect* rect) const { |
| return mapAndClipRect(*rect, rect); |
| } |
| bool mapAndClipRect(const SkRect& src, SkRect* mapped) const; |
| |
| void clipRect(const DlRect& rect, ClipOp op, bool is_aa); |
| void clipRect(const SkRect& rect, ClipOp op, bool is_aa) { |
| clipRect(ToDlRect(rect), op, is_aa); |
| } |
| void clipRRect(const SkRRect& rrect, ClipOp op, bool is_aa); |
| void clipPath(const SkPath& path, ClipOp op, bool is_aa); |
| |
| private: |
| DlRect cull_rect_; |
| DlMatrix matrix_; |
| |
| void adjustCullRect(const DlRect& clip, ClipOp op, bool is_aa); |
| |
| friend class DisplayListMatrixClipTracker; |
| }; |
| |
| class DisplayListMatrixClipTracker { |
| private: |
| using ClipOp = DlCanvas::ClipOp; |
| using DlRect = impeller::Rect; |
| using DlMatrix = impeller::Matrix; |
| |
| public: |
| DisplayListMatrixClipTracker(const DlRect& cull_rect, const DlMatrix& matrix); |
| DisplayListMatrixClipTracker(const SkRect& cull_rect, const SkMatrix& matrix); |
| DisplayListMatrixClipTracker(const SkRect& cull_rect, const SkM44& matrix); |
| |
| // These methods should almost never be used as they breaks the encapsulation |
| // of the enclosing clips. However they are needed for practical purposes in |
| // some rare cases - such as when a saveLayer is collecting rendering |
| // operations prior to applying a filter on the entire layer bounds and |
| // some of those operations fall outside the enclosing clip, but their |
| // filtered content will spread out from where they were rendered on the |
| // layer into the enclosing clipped area. |
| // Omitting the |cull_rect| argument, or passing nullptr, will restore the |
| // cull rect to the initial value it had when the tracker was constructed. |
| void resetDeviceCullRect(const DlRect* cull_rect = nullptr) { |
| if (cull_rect) { |
| current_->resetDeviceCullRect(*cull_rect); |
| } else { |
| current_->resetDeviceCullRect(saved_[0].cull_rect_); |
| } |
| } |
| void resetDeviceCullRect(const SkRect* cull_rect = nullptr) { |
| if (cull_rect) { |
| current_->resetDeviceCullRect(*cull_rect); |
| } else { |
| current_->resetDeviceCullRect(saved_[0].cull_rect_); |
| } |
| } |
| void resetLocalCullRect(const DlRect* cull_rect = nullptr) { |
| if (cull_rect) { |
| current_->resetLocalCullRect(*cull_rect); |
| } else { |
| current_->resetDeviceCullRect(saved_[0].cull_rect_); |
| } |
| } |
| void resetLocalCullRect(const SkRect* cull_rect = nullptr) { |
| if (cull_rect) { |
| current_->resetLocalCullRect(*cull_rect); |
| } else { |
| current_->resetDeviceCullRect(saved_[0].cull_rect_); |
| } |
| } |
| |
| static bool is_3x3(const SkM44& m44); |
| |
| SkRect base_device_cull_rect() const { return saved_[0].device_cull_rect(); } |
| |
| bool using_4x4_matrix() const { return current_->using_4x4_matrix(); } |
| |
| DlMatrix matrix() const { return current_->matrix(); } |
| SkM44 matrix_4x4() const { return current_->matrix_4x4(); } |
| SkMatrix matrix_3x3() const { return current_->matrix_3x3(); } |
| SkRect local_cull_rect() const { return current_->local_cull_rect(); } |
| SkRect device_cull_rect() const { return current_->device_cull_rect(); } |
| bool content_culled(const SkRect& content_bounds) const { |
| return current_->content_culled(content_bounds); |
| } |
| bool is_cull_rect_empty() const { return current_->is_cull_rect_empty(); } |
| |
| void save(); |
| void restore(); |
| void reset(); |
| int getSaveCount() { |
| // saved_[0] is always the untouched initial conditions |
| // saved_[1] is the first editable stack entry |
| return saved_.size() - 1; |
| } |
| void restoreToCount(int restore_count); |
| |
| void translate(SkScalar tx, SkScalar ty) { current_->translate(tx, ty); } |
| void scale(SkScalar sx, SkScalar sy) { current_->scale(sx, sy); } |
| void skew(SkScalar skx, SkScalar sky) { current_->skew(skx, sky); } |
| void rotate(SkScalar degrees) { current_->rotate(degrees); } |
| void transform(const DlMatrix& matrix) { current_->transform(matrix); } |
| void transform(const SkM44& m44) { current_->transform(m44); } |
| void transform(const SkMatrix& matrix) { current_->transform(matrix); } |
| // clang-format off |
| void transform2DAffine( |
| SkScalar mxx, SkScalar mxy, SkScalar mxt, |
| SkScalar myx, SkScalar myy, SkScalar myt) { |
| current_->transform2DAffine(mxx, mxy, mxt, myx, myy, myt); |
| } |
| void transformFullPerspective( |
| SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, |
| SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, |
| SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, |
| SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) { |
| current_->transformFullPerspective( |
| mxx, mxy, mxz, mxt, |
| myx, myy, myz, myt, |
| mzx, mzy, mzz, mzt, |
| mwx, mwy, mwz, mwt |
| ); |
| } |
| // clang-format on |
| void setTransform(const DlMatrix& matrix) { current_->setTransform(matrix); } |
| void setTransform(const SkMatrix& matrix) { current_->setTransform(matrix); } |
| void setTransform(const SkM44& m44) { current_->setTransform(m44); } |
| void setIdentity() { current_->setIdentity(); } |
| // If the matrix in |other_tracker| is invertible then transform this |
| // tracker by the inverse of its matrix and return true. Otherwise, |
| // return false and leave this tracker unmodified. |
| bool inverseTransform(const DisplayListMatrixClipTracker& other_tracker) { |
| return current_->inverseTransform(*other_tracker.current_); |
| } |
| |
| bool mapRect(DlRect* rect) const { return current_->mapRect(*rect, rect); } |
| bool mapRect(const DlRect& src, DlRect* mapped) { |
| return current_->mapRect(src, mapped); |
| } |
| bool mapRect(SkRect* rect) const { return current_->mapRect(*rect, rect); } |
| bool mapRect(const SkRect& src, SkRect* mapped) { |
| return current_->mapRect(src, mapped); |
| } |
| |
| void clipRect(const DlRect& rect, ClipOp op, bool is_aa) { |
| current_->clipRect(rect, op, is_aa); |
| } |
| void clipRect(const SkRect& rect, ClipOp op, bool is_aa) { |
| current_->clipRect(rect, op, is_aa); |
| } |
| void clipRRect(const SkRRect& rrect, ClipOp op, bool is_aa) { |
| current_->clipRRect(rrect, op, is_aa); |
| } |
| void clipPath(const SkPath& path, ClipOp op, bool is_aa) { |
| current_->clipPath(path, op, is_aa); |
| } |
| |
| private: |
| DisplayListMatrixClipState* current_; |
| std::vector<DisplayListMatrixClipState> saved_; |
| }; |
| |
| } // namespace flutter |
| |
| #endif // FLUTTER_DISPLAY_LIST_UTILS_DL_MATRIX_CLIP_TRACKER_H_ |