// 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_DL_OP_FLAGS_H_
#define FLUTTER_DISPLAY_LIST_DL_OP_FLAGS_H_

#include "flutter/display_list/dl_paint.h"
#include "flutter/fml/logging.h"

namespace flutter {

class DlPathEffect;
/// The base class for the classes that maintain a list of
/// attributes that might be important for a number of operations
/// including which rendering attributes need to be set before
/// calling a rendering method (all |drawSomething| calls),
/// or for determining which exceptional conditions may need
/// to be accounted for in bounds calculations.
/// This class contains only protected definitions and helper methods
/// for the public classes |DisplayListAttributeFlags| and
/// |DisplayListSpecialGeometryFlags|.
class DisplayListFlags {
 protected:
  // A drawing operation that is not geometric in nature (but which
  // may still apply a MaskFilter - see |kUsesMaskFilter_| below).
  static constexpr int kIsNonGeometric_ = 0;

  // A geometric operation that is defined as a fill operation
  // regardless of what the current paint Style is set to.
  // This flag will automatically assume |kUsesMaskFilter_|.
  static constexpr int kIsFilledGeometry_ = 1 << 0;

  // A geometric operation that is defined as a stroke operation
  // regardless of what the current paint Style is set to.
  // This flag will automatically assume |kUsesMaskFilter_|.
  static constexpr int kIsStrokedGeometry_ = 1 << 1;

  // A geometric operation that may be a stroke or fill operation
  // depending on the current state of the paint Style attribute.
  // This flag will automatically assume |kUsesMaskFilter_|.
  static constexpr int kIsDrawnGeometry_ = 1 << 2;

  static constexpr int kIsAnyGeometryMask_ =  //
      kIsFilledGeometry_ |                    //
      kIsStrokedGeometry_ |                   //
      kIsDrawnGeometry_;

  // A primitive that floods the surface (or clip) with no
  // natural bounds, such as |drawColor| or |drawPaint|.
  static constexpr int kFloodsSurface_ = 1 << 3;

  static constexpr int kMayHaveCaps_ = 1 << 4;
  static constexpr int kMayHaveJoins_ = 1 << 5;
  static constexpr int kButtCapIsSquare_ = 1 << 6;

  // A geometric operation which has a path that might have
  // end caps that are not rectilinear which means that square
  // end caps might project further than half the stroke width
  // from the geometry bounds.
  // A rectilinear path such as |drawRect| will not have
  // diagonal end caps. |drawLine| might have diagonal end
  // caps depending on the angle of the line, and more likely
  // |drawPath| will often have such end caps.
  static constexpr int kMayHaveDiagonalCaps_ = 1 << 7;

  // A geometric operation which has joined vertices that are
  // not guaranteed to be smooth (angles of incoming and outgoing)
  // segments at some joins may not have the same angle) or
  // rectilinear (squares have right angles at the corners, but
  // those corners will never extend past the bounding box of
  // the geometry pre-transform).
  // |drawRect|, |drawOval| and |drawRRect| all have well
  // behaved joins, but |drawPath| might have joins that cause
  // mitered extensions outside the pre-transformed bounding box.
  static constexpr int kMayHaveAcuteJoins_ = 1 << 8;

  static constexpr int kAnySpecialGeometryMask_ =           //
      kMayHaveCaps_ | kMayHaveJoins_ | kButtCapIsSquare_ |  //
      kMayHaveDiagonalCaps_ | kMayHaveAcuteJoins_;

  // clang-format off
  static constexpr int kUsesAntiAlias_       = 1 << 10;
  static constexpr int kUsesDither_          = 1 << 11;
  static constexpr int kUsesAlpha_           = 1 << 12;
  static constexpr int kUsesColor_           = 1 << 13;
  static constexpr int kUsesBlend_           = 1 << 14;
  static constexpr int kUsesShader_          = 1 << 15;
  static constexpr int kUsesColorFilter_     = 1 << 16;
  static constexpr int kUsesPathEffect_      = 1 << 17;
  static constexpr int kUsesMaskFilter_      = 1 << 18;
  static constexpr int kUsesImageFilter_     = 1 << 19;

  // Some ops have an optional paint argument. If the version
  // stored in the DisplayList ignores the paint, but there
  // is an option to render the same op with a paint then
  // both of the following flags are set to indicate that
  // a default paint object can be constructed when rendering
  // the op to carry information imposed from outside the
  // DisplayList (for example, the opacity override).
  static constexpr int kIgnoresPaint_        = 1 << 30;
  // clang-format on

  static constexpr int kAnyAttributeMask_ =  //
      kUsesAntiAlias_ | kUsesDither_ | kUsesAlpha_ | kUsesColor_ | kUsesBlend_ |
      kUsesShader_ | kUsesColorFilter_ | kUsesPathEffect_ | kUsesMaskFilter_ |
      kUsesImageFilter_;
};

class DisplayListFlagsBase : protected DisplayListFlags {
 protected:
  explicit constexpr DisplayListFlagsBase(int flags) : flags_(flags) {}

  const int flags_;

  constexpr bool has_any(int qFlags) const { return (flags_ & qFlags) != 0; }
  constexpr bool has_all(int qFlags) const {
    return (flags_ & qFlags) == qFlags;
  }
  constexpr bool has_none(int qFlags) const { return (flags_ & qFlags) == 0; }
};

/// An attribute class for advertising specific properties of
/// a geometric attribute that can affect the computation of
/// the bounds of the primitive.
class DisplayListSpecialGeometryFlags : DisplayListFlagsBase {
 public:
  /// The geometry may have segments that end without closing the path.
  constexpr bool may_have_end_caps() const { return has_any(kMayHaveCaps_); }

  /// The geometry may have segments connect non-continuously.
  constexpr bool may_have_joins() const { return has_any(kMayHaveJoins_); }

  /// Mainly for drawPoints(PointMode) where Butt caps are rendered as squares.
  constexpr bool butt_cap_becomes_square() const {
    return has_any(kButtCapIsSquare_);
  }

  /// The geometry may have segments that end on a diagonal
  /// such that their end caps extend further than the default
  /// |strokeWidth * 0.5| margin around the geometry.
  constexpr bool may_have_diagonal_caps() const {
    return has_any(kMayHaveDiagonalCaps_);
  }

  /// The geometry may have segments that meet at vertices at
  /// an acute angle such that the miter joins will extend
  /// further than the default |strokeWidth * 0.5| margin around
  /// the geometry.
  constexpr bool may_have_acute_joins() const {
    return has_any(kMayHaveAcuteJoins_);
  }

 private:
  explicit constexpr DisplayListSpecialGeometryFlags(int flags)
      : DisplayListFlagsBase(flags) {
    FML_DCHECK((flags & kAnySpecialGeometryMask_) == flags);
  }

  const DisplayListSpecialGeometryFlags with(int extra) const {
    return extra == 0 ? *this : DisplayListSpecialGeometryFlags(flags_ | extra);
  }

  friend class DisplayListAttributeFlags;
};

class DisplayListAttributeFlags : DisplayListFlagsBase {
 public:
  const DisplayListSpecialGeometryFlags WithPathEffect(
      const DlPathEffect* effect,
      bool is_stroked) const;

  constexpr bool ignores_paint() const { return has_any(kIgnoresPaint_); }

  constexpr bool applies_anti_alias() const { return has_any(kUsesAntiAlias_); }
  constexpr bool applies_dither() const { return has_any(kUsesDither_); }
  constexpr bool applies_color() const { return has_any(kUsesColor_); }
  constexpr bool applies_alpha() const { return has_any(kUsesAlpha_); }
  constexpr bool applies_alpha_or_color() const {
    return has_any(kUsesAlpha_ | kUsesColor_);
  }

  /// The primitive dynamically determines whether it is a stroke or fill
  /// operation (or both) based on the setting of the |Style| attribute.
  constexpr bool applies_style() const { return has_any(kIsDrawnGeometry_); }
  /// The primitive can use any of the stroke attributes, such as
  /// StrokeWidth, StrokeMiter, StrokeCap, or StrokeJoin. This
  /// method will return if the primitive is defined as one that
  /// strokes its geometry (such as |drawLine|) or if it is defined
  /// as one that honors the Style attribute. If the Style attribute
  /// is known then a more accurate answer can be returned from
  /// the |is_stroked| method by supplying the actual setting of
  /// the style.
  // bool applies_stroke_attributes() const { return is_stroked(); }

  constexpr bool applies_shader() const { return has_any(kUsesShader_); }
  /// The primitive honors the current DlColorFilter, including
  /// the related attribute InvertColors
  constexpr bool applies_color_filter() const {
    return has_any(kUsesColorFilter_);
  }
  /// The primitive honors the DlBlendMode
  constexpr bool applies_blend() const { return has_any(kUsesBlend_); }
  constexpr bool applies_path_effect() const {
    return has_any(kUsesPathEffect_);
  }
  /// The primitive honors the DlMaskFilter whether set using the
  /// filter object or using the convenience method |setMaskBlurFilter|
  constexpr bool applies_mask_filter() const {
    return has_any(kUsesMaskFilter_);
  }
  constexpr bool applies_image_filter() const {
    return has_any(kUsesImageFilter_);
  }

  constexpr bool is_geometric() const { return has_any(kIsAnyGeometryMask_); }
  constexpr bool always_stroked() const { return has_any(kIsStrokedGeometry_); }
  constexpr bool is_stroked(DlDrawStyle style = DlDrawStyle::kStroke) const {
    return (has_any(kIsStrokedGeometry_) ||
            (style != DlDrawStyle::kFill && has_any(kIsDrawnGeometry_)));
  }

  constexpr bool is_flood() const { return has_any(kFloodsSurface_); }

  constexpr bool operator==(DisplayListAttributeFlags const& other) const {
    return flags_ == other.flags_;
  }

 private:
  explicit constexpr DisplayListAttributeFlags(int flags)
      : DisplayListFlagsBase(flags),
        special_flags_(flags & kAnySpecialGeometryMask_) {
    FML_DCHECK((flags & kIsAnyGeometryMask_) == kIsNonGeometric_ ||
               (flags & kIsAnyGeometryMask_) == kIsFilledGeometry_ ||
               (flags & kIsAnyGeometryMask_) == kIsStrokedGeometry_ ||
               (flags & kIsAnyGeometryMask_) == kIsDrawnGeometry_);
    FML_DCHECK(((flags & kAnyAttributeMask_) == 0) !=
               ((flags & kIgnoresPaint_) == 0));
    FML_DCHECK((flags & kIsAnyGeometryMask_) != 0 ||
               (flags & kAnySpecialGeometryMask_) == 0);
  }

  constexpr DisplayListAttributeFlags operator+(int extra) const {
    return extra == 0 ? *this : DisplayListAttributeFlags(flags_ | extra);
  }

  constexpr DisplayListAttributeFlags operator-(int remove) const {
    FML_DCHECK(has_all(remove));
    return DisplayListAttributeFlags(flags_ & ~remove);
  }

  const DisplayListSpecialGeometryFlags special_flags_;

  friend class DisplayListOpFlags;
};

class DisplayListOpFlags : DisplayListFlags {
 private:
  // Flags common to all primitives that apply colors
  static constexpr int kBASE_PaintFlags_ = (kUsesDither_ |       //
                                            kUsesColor_ |        //
                                            kUsesAlpha_ |        //
                                            kUsesBlend_ |        //
                                            kUsesShader_ |       //
                                            kUsesColorFilter_ |  //
                                            kUsesImageFilter_);

  // Flags common to all primitives that stroke or fill
  static constexpr int kBASE_StrokeOrFillFlags_ = (kIsDrawnGeometry_ |  //
                                                   kUsesAntiAlias_ |    //
                                                   kUsesMaskFilter_ |   //
                                                   kUsesPathEffect_);

  // Flags common to primitives that stroke geometry
  static constexpr int kBASE_StrokeFlags_ = (kIsStrokedGeometry_ |  //
                                             kUsesAntiAlias_ |      //
                                             kUsesMaskFilter_ |     //
                                             kUsesPathEffect_);

  // Flags common to primitives that render an image with paint attributes
  static constexpr int kBASE_ImageFlags_ = (kIsNonGeometric_ |   //
                                            kUsesAlpha_ |        //
                                            kUsesDither_ |       //
                                            kUsesBlend_ |        //
                                            kUsesColorFilter_ |  //
                                            kUsesImageFilter_);

 public:
  static constexpr DisplayListAttributeFlags kSaveLayerFlags{
      kIgnoresPaint_  //
  };
  static constexpr DisplayListAttributeFlags kSaveLayerWithPaintFlags{
      kIsNonGeometric_ |   //
      kUsesAlpha_ |        //
      kUsesBlend_ |        //
      kUsesColorFilter_ |  //
      kUsesImageFilter_    //
  };
  static constexpr DisplayListAttributeFlags kDrawColorFlags{
      kFloodsSurface_ |  //
      kIgnoresPaint_     //
  };
  static constexpr DisplayListAttributeFlags kDrawPaintFlags{
      kBASE_PaintFlags_ |  //
      kFloodsSurface_      //
  };
  // Special case flags for horizonal and vertical lines
  static constexpr DisplayListAttributeFlags kDrawHVLineFlags{
      kBASE_PaintFlags_ |   //
      kBASE_StrokeFlags_ |  //
      kMayHaveCaps_         //
  };
  static constexpr DisplayListAttributeFlags kDrawLineFlags{
      kDrawHVLineFlags         //
      + kMayHaveDiagonalCaps_  //
  };
  static constexpr DisplayListAttributeFlags kDrawRectFlags{
      kBASE_PaintFlags_ |         //
      kBASE_StrokeOrFillFlags_ |  //
      kMayHaveJoins_              //
  };
  static constexpr DisplayListAttributeFlags kDrawOvalFlags{
      kBASE_PaintFlags_ |       //
      kBASE_StrokeOrFillFlags_  //
  };
  static constexpr DisplayListAttributeFlags kDrawCircleFlags{
      kBASE_PaintFlags_ |       //
      kBASE_StrokeOrFillFlags_  //
  };
  static constexpr DisplayListAttributeFlags kDrawRRectFlags{
      kBASE_PaintFlags_ |       //
      kBASE_StrokeOrFillFlags_  //
  };
  static constexpr DisplayListAttributeFlags kDrawDRRectFlags{
      kBASE_PaintFlags_ |       //
      kBASE_StrokeOrFillFlags_  //
  };
  static constexpr DisplayListAttributeFlags kDrawPathFlags{
      kBASE_PaintFlags_ |         //
      kBASE_StrokeOrFillFlags_ |  //
      kMayHaveCaps_ |             //
      kMayHaveDiagonalCaps_ |     //
      kMayHaveJoins_ |            //
      kMayHaveAcuteJoins_         //
  };
  static constexpr DisplayListAttributeFlags kDrawArcNoCenterFlags{
      kBASE_PaintFlags_ |         //
      kBASE_StrokeOrFillFlags_ |  //
      kMayHaveCaps_ |             //
      kMayHaveDiagonalCaps_       //
  };
  static constexpr DisplayListAttributeFlags kDrawArcWithCenterFlags{
      kBASE_PaintFlags_ |         //
      kBASE_StrokeOrFillFlags_ |  //
      kMayHaveJoins_ |            //
      kMayHaveAcuteJoins_         //
  };
  static constexpr DisplayListAttributeFlags kDrawPointsAsPointsFlags{
      kBASE_PaintFlags_ |   //
      kBASE_StrokeFlags_ |  //
      kMayHaveCaps_ |       //
      kButtCapIsSquare_     //
  };
  static constexpr DisplayListAttributeFlags kDrawPointsAsLinesFlags{
      kBASE_PaintFlags_ |    //
      kBASE_StrokeFlags_ |   //
      kMayHaveCaps_ |        //
      kMayHaveDiagonalCaps_  //
  };
  // Polygon mode just draws (count-1) separate lines, no joins
  static constexpr DisplayListAttributeFlags kDrawPointsAsPolygonFlags{
      kBASE_PaintFlags_ |    //
      kBASE_StrokeFlags_ |   //
      kMayHaveCaps_ |        //
      kMayHaveDiagonalCaps_  //
  };
  static constexpr DisplayListAttributeFlags kDrawVerticesFlags{
      kIsNonGeometric_ |   //
      kUsesDither_ |       //
      kUsesAlpha_ |        //
      kUsesShader_ |       //
      kUsesBlend_ |        //
      kUsesColorFilter_ |  //
      kUsesImageFilter_    //
  };
  static constexpr DisplayListAttributeFlags kDrawImageFlags{
      kIgnoresPaint_  //
  };
  static constexpr DisplayListAttributeFlags kDrawImageWithPaintFlags{
      kBASE_ImageFlags_ |  //
      kUsesAntiAlias_ |    //
      kUsesMaskFilter_     //
  };
  static constexpr DisplayListAttributeFlags kDrawImageRectFlags{
      kIgnoresPaint_  //
  };
  static constexpr DisplayListAttributeFlags kDrawImageRectWithPaintFlags{
      kBASE_ImageFlags_ |  //
      kUsesAntiAlias_ |    //
      kUsesMaskFilter_     //
  };
  static constexpr DisplayListAttributeFlags kDrawImageNineFlags{
      kIgnoresPaint_  //
  };
  static constexpr DisplayListAttributeFlags kDrawImageNineWithPaintFlags{
      kBASE_ImageFlags_  //
  };
  static constexpr DisplayListAttributeFlags kDrawAtlasFlags{
      kIgnoresPaint_  //
  };
  static constexpr DisplayListAttributeFlags kDrawAtlasWithPaintFlags{
      kBASE_ImageFlags_  //
  };
  static constexpr DisplayListAttributeFlags kDrawDisplayListFlags{
      kIgnoresPaint_  //
  };
  static constexpr DisplayListAttributeFlags kDrawTextBlobFlags{
      DisplayListAttributeFlags(kBASE_PaintFlags_ |         //
                                kBASE_StrokeOrFillFlags_ |  //
                                kMayHaveJoins_)             //
      - kUsesAntiAlias_                                     //
  };
  static constexpr DisplayListAttributeFlags kDrawShadowFlags{
      kIgnoresPaint_  //
  };
};

}  // namespace flutter

#endif  // FLUTTER_DISPLAY_LIST_DL_OP_FLAGS_H_
