summaryrefslogtreecommitdiffstats
path: root/layout/painting/DisplayItemClip.h
blob: ff9df266035df22f3962f4b3c7135201c9333091 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef DISPLAYITEMCLIP_H_
#define DISPLAYITEMCLIP_H_

#include "mozilla/RefPtr.h"
#include "nsRect.h"
#include "nsTArray.h"

class gfxContext;
class nsPresContext;
class nsRegion;

namespace mozilla {
namespace gfx {
class DrawTarget;
class Path;
}  // namespace gfx
namespace layers {
class StackingContextHelper;
}  // namespace layers
namespace wr {
struct ComplexClipRegion;
}  // namespace wr
}  // namespace mozilla

namespace mozilla {

/**
 * An DisplayItemClip represents the intersection of an optional rectangle
 * with a list of rounded rectangles (which is often empty), all in appunits.
 * It can represent everything CSS clipping can do to an element (except for
 * SVG clip-path), including no clipping at all.
 */
class DisplayItemClip {
  typedef mozilla::gfx::DeviceColor DeviceColor;
  typedef mozilla::gfx::DrawTarget DrawTarget;
  typedef mozilla::gfx::Path Path;

 public:
  struct RoundedRect {
    nsRect mRect;
    // Indices into mRadii are the HalfCorner values in gfx/2d/Types.h
    nscoord mRadii[8];

    RoundedRect operator+(const nsPoint& aOffset) const {
      RoundedRect r = *this;
      r.mRect += aOffset;
      return r;
    }
    bool operator==(const RoundedRect& aOther) const {
      if (!mRect.IsEqualInterior(aOther.mRect)) {
        return false;
      }

      for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
        if (mRadii[corner] != aOther.mRadii[corner]) {
          return false;
        }
      }
      return true;
    }
    bool operator!=(const RoundedRect& aOther) const {
      return !(*this == aOther);
    }
  };

  // Constructs a DisplayItemClip that does no clipping at all.
  DisplayItemClip() : mHaveClipRect(false) {}

  void SetTo(const nsRect& aRect);
  void SetTo(const nsRect& aRect, const nscoord* aRadii);
  void SetTo(const nsRect& aRect, const nsRect& aRoundedRect,
             const nscoord* aRadii);
  void IntersectWith(const DisplayItemClip& aOther);

  // Apply this |DisplayItemClip| to the given gfxContext.  Any saving of state
  // or clearing of other clips must be done by the caller.
  // See aBegin/aEnd note on ApplyRoundedRectsTo.
  void ApplyTo(gfxContext* aContext, int32_t A2D) const;

  void ApplyRectTo(gfxContext* aContext, int32_t A2D) const;
  // Applies the rounded rects in this Clip to aContext
  // Will only apply rounded rects from aBegin (inclusive) to aEnd
  // (exclusive) or the number of rounded rects, whichever is smaller.
  void ApplyRoundedRectClipsTo(gfxContext* aContext, int32_t A2DPRInt32,
                               uint32_t aBegin, uint32_t aEnd) const;

  // Draw (fill) the rounded rects in this clip to aContext
  void FillIntersectionOfRoundedRectClips(gfxContext* aContext,
                                          const DeviceColor& aColor,
                                          int32_t aAppUnitsPerDevPixel) const;
  // 'Draw' (create as a path, does not stroke or fill) aRoundRect to aContext
  already_AddRefed<Path> MakeRoundedRectPath(
      DrawTarget& aDrawTarget, int32_t A2D,
      const RoundedRect& aRoundRect) const;

  // Returns true if the intersection of aRect and this clip region is
  // non-empty. This is precise for DisplayItemClips with at most one
  // rounded rectangle. When multiple rounded rectangles are present, we just
  // check that the rectangle intersects all of them (but possibly in different
  // places). So it may return true when the correct answer is false.
  bool MayIntersect(const nsRect& aRect) const;

  // Return a rectangle contained in the intersection of aRect with this
  // clip region. Tries to return the largest possible rectangle, but may
  // not succeed.
  nsRect ApproximateIntersectInward(const nsRect& aRect) const;

  /*
   * Computes a region which contains the clipped area of this DisplayItemClip,
   * or if aOldClip is non-null, the union of the clipped area of this
   * DisplayItemClip with the clipped area of aOldClip translated by aShift.
   * The result is stored in aCombined. If the result would be infinite
   * (because one or both of the clips does no clipping), returns false.
   */
  bool ComputeRegionInClips(const DisplayItemClip* aOldClip,
                            const nsPoint& aShift, nsRegion* aCombined) const;

  // Returns false if aRect is definitely not clipped by a rounded corner in
  // this clip. Returns true if aRect is clipped by a rounded corner in this
  // clip or it can not be quickly determined that it is not clipped by a
  // rounded corner in this clip.
  bool IsRectClippedByRoundedCorner(const nsRect& aRect) const;

  // Returns false if aRect is definitely not clipped by anything in this clip.
  // Fast but not necessarily accurate.
  bool IsRectAffectedByClip(const nsRect& aRect) const;
  bool IsRectAffectedByClip(const nsIntRect& aRect, float aXScale,
                            float aYScale, int32_t A2D) const;

  // Intersection of all rects in this clip ignoring any rounded corners.
  nsRect NonRoundedIntersection() const;

  // Intersect the given rects with all rects in this clip, ignoring any
  // rounded corners.
  nsRect ApplyNonRoundedIntersection(const nsRect& aRect) const;

  // Gets rid of any rounded corners in this clip.
  void RemoveRoundedCorners();

  // Adds the difference between Intersect(*this + aPoint, aBounds) and
  // Intersect(aOther, aOtherBounds) to aDifference (or a bounding-box thereof).
  void AddOffsetAndComputeDifference(const nsPoint& aPoint,
                                     const nsRect& aBounds,
                                     const DisplayItemClip& aOther,
                                     const nsRect& aOtherBounds,
                                     nsRegion* aDifference);

  bool operator==(const DisplayItemClip& aOther) const {
    return mHaveClipRect == aOther.mHaveClipRect &&
           (!mHaveClipRect || mClipRect.IsEqualInterior(aOther.mClipRect)) &&
           mRoundedClipRects == aOther.mRoundedClipRects;
  }
  bool operator!=(const DisplayItemClip& aOther) const {
    return !(*this == aOther);
  }

  bool HasClip() const { return mHaveClipRect; }
  const nsRect& GetClipRect() const {
    NS_ASSERTION(HasClip(), "No clip rect!");
    return mClipRect;
  }

  void MoveBy(const nsPoint& aPoint);

  nsCString ToString() const;

  uint32_t GetRoundedRectCount() const { return mRoundedClipRects.Length(); }
  void AppendRoundedRects(nsTArray<RoundedRect>* aArray) const;

  void ToComplexClipRegions(int32_t aAppUnitsPerDevPixel,
                            nsTArray<wr::ComplexClipRegion>& aOutArray) const;

  static const DisplayItemClip& NoClip();

  static void Shutdown();

 private:
  nsRect mClipRect;
  CopyableTArray<RoundedRect> mRoundedClipRects;
  // If mHaveClipRect is false then this object represents no clipping at all
  // and mRoundedClipRects must be empty.
  bool mHaveClipRect;
};

}  // namespace mozilla

#endif /* DISPLAYITEMCLIP_H_ */