1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef MASKLAYERIMAGECACHE_H_
8 #define MASKLAYERIMAGECACHE_H_
9 
10 #include "DisplayItemClip.h"
11 #include "nsAutoPtr.h"
12 #include "nsPresContext.h"
13 #include "mozilla/gfx/Matrix.h"
14 
15 namespace mozilla {
16 
17 namespace layers {
18 class ImageContainer;
19 class KnowsCompositor;
20 }  // namespace layers
21 
22 /**
23  * Keeps a record of image containers for mask layers, containers are mapped
24  * from the rounded rects used to create them.
25  * The cache stores MaskLayerImageEntries indexed by MaskLayerImageKeys.
26  * Each MaskLayerImageEntry owns a heap-allocated MaskLayerImageKey
27  * (heap-allocated so that a mask layer's userdata can keep a pointer to the
28  * key for its image, in spite of the hashtable moving its entries around).
29  * The key consists of the rounded rects used to create the mask,
30  * an nsRefPtr to the ImageContainer containing the image, and a count
31  * of the number of layers currently using this ImageContainer.
32  * When the key's layer count is zero, the cache
33  * may remove the entry, which deletes the key object.
34  */
35 class MaskLayerImageCache {
36   typedef mozilla::layers::ImageContainer ImageContainer;
37   typedef mozilla::layers::KnowsCompositor KnowsCompositor;
38 
39  public:
40   MaskLayerImageCache();
41   ~MaskLayerImageCache();
42 
43   /**
44    * Representation of a rounded rectangle in device pixel coordinates, in
45    * contrast to DisplayItemClip::RoundedRect, which uses app units.
46    * In particular, our internal representation uses a gfxRect, rather than
47    * an nsRect, so this class is easier to use with transforms.
48    */
49   struct PixelRoundedRect {
PixelRoundedRectPixelRoundedRect50     PixelRoundedRect(const DisplayItemClip::RoundedRect& aRRect,
51                      nsPresContext* aPresContext)
52         : mRect(aPresContext->AppUnitsToGfxUnits(aRRect.mRect.x),
53                 aPresContext->AppUnitsToGfxUnits(aRRect.mRect.y),
54                 aPresContext->AppUnitsToGfxUnits(aRRect.mRect.width),
55                 aPresContext->AppUnitsToGfxUnits(aRRect.mRect.height)) {
56       MOZ_COUNT_CTOR(PixelRoundedRect);
57       NS_FOR_CSS_HALF_CORNERS(corner) {
58         mRadii[corner] =
59             aPresContext->AppUnitsToGfxUnits(aRRect.mRadii[corner]);
60       }
61     }
PixelRoundedRectPixelRoundedRect62     PixelRoundedRect(const PixelRoundedRect& aPRR) : mRect(aPRR.mRect) {
63       MOZ_COUNT_CTOR(PixelRoundedRect);
64       NS_FOR_CSS_HALF_CORNERS(corner) { mRadii[corner] = aPRR.mRadii[corner]; }
65     }
66 
~PixelRoundedRectPixelRoundedRect67     ~PixelRoundedRect() { MOZ_COUNT_DTOR(PixelRoundedRect); }
68 
69     // Applies the scale and translate components of aTransform.
70     // It is an error to pass a matrix which does more than just scale
71     // and translate.
ScaleAndTranslatePixelRoundedRect72     void ScaleAndTranslate(const gfx::Matrix& aTransform) {
73       NS_ASSERTION(aTransform._12 == 0 && aTransform._21 == 0,
74                    "Transform has a component other than scale and translate");
75 
76       mRect = aTransform.TransformBounds(mRect);
77 
78       for (size_t i = 0; i < ArrayLength(mRadii); i += 2) {
79         mRadii[i] *= aTransform._11;
80         mRadii[i + 1] *= aTransform._22;
81       }
82     }
83 
84     bool operator==(const PixelRoundedRect& aOther) const {
85       if (!mRect.IsEqualInterior(aOther.mRect)) {
86         return false;
87       }
88 
NS_FOR_CSS_HALF_CORNERSPixelRoundedRect89       NS_FOR_CSS_HALF_CORNERS(corner) {
90         if (mRadii[corner] != aOther.mRadii[corner]) {
91           return false;
92         }
93       }
94       return true;
95     }
96     bool operator!=(const PixelRoundedRect& aOther) const {
97       return !(*this == aOther);
98     }
99 
100     // Create a hash for this object.
HashPixelRoundedRect101     PLDHashNumber Hash() const {
102       PLDHashNumber hash = HashBytes(&mRect.x, 4 * sizeof(gfxFloat));
103       hash = AddToHash(hash, HashBytes(mRadii, 8 * sizeof(gfxFloat)));
104 
105       return hash;
106     }
107 
108     gfx::Rect mRect;
109     // Indices into mRadii are the enum HalfCorner constants in gfx/2d/Types.h
110     gfxFloat mRadii[8];
111 
112    private:
113     PixelRoundedRect() = delete;
114   };
115 
116   struct MaskLayerImageKeyRef;
117 
118   /**
119    * A key to identify cached image containers.
120    * The const-ness of this class is with respect to its use as a key into a
121    * hashtable, so anything not used to create the hash is mutable.
122    * mLayerCount counts the number of mask layers which have a reference to
123    * MaskLayerImageEntry::mContainer; it is maintained by MaskLayerUserData,
124    * which keeps a reference to the key. There will usually be mLayerCount + 1
125    * pointers to a key object (the +1 being from the hashtable entry), but this
126    * invariant may be temporarily broken.
127    */
128   struct MaskLayerImageKey {
129     friend struct MaskLayerImageKeyRef;
130 
131     MaskLayerImageKey();
132     MaskLayerImageKey(const MaskLayerImageKey& aKey);
133 
134     ~MaskLayerImageKey();
135 
HasZeroLayerCountMaskLayerImageKey136     bool HasZeroLayerCount() const { return mLayerCount == 0; }
137 
HashMaskLayerImageKey138     PLDHashNumber Hash() const {
139       PLDHashNumber hash = 0;
140 
141       for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) {
142         hash = AddToHash(hash, mRoundedClipRects[i].Hash());
143       }
144       hash = AddToHash(hash, mKnowsCompositor.get());
145 
146       return hash;
147     }
148 
149     bool operator==(const MaskLayerImageKey& aOther) const {
150       return mKnowsCompositor == aOther.mKnowsCompositor &&
151              mRoundedClipRects == aOther.mRoundedClipRects;
152     }
153 
154     nsTArray<PixelRoundedRect> mRoundedClipRects;
155     RefPtr<KnowsCompositor> mKnowsCompositor;
156 
157    private:
IncLayerCountMaskLayerImageKey158     void IncLayerCount() const { ++mLayerCount; }
DecLayerCountMaskLayerImageKey159     void DecLayerCount() const {
160       NS_ASSERTION(mLayerCount > 0, "Inconsistent layer count");
161       --mLayerCount;
162     }
163     mutable uint32_t mLayerCount;
164   };
165 
166   /**
167    * This struct maintains a reference to a MaskLayerImageKey, via a variant on
168    * refcounting. When a key is passed in via Reset(), we increment the
169    * passed-in key's mLayerCount, and we decrement its mLayerCount when we're
170    * destructed (or when the key is replaced via a second Reset() call).
171    *
172    * However, unlike standard refcounting smart-pointers, this object does
173    * *not* delete the tracked MaskLayerImageKey -- instead, deletion happens
174    * in MaskLayerImageCache::Sweep(), for any keys whose mLayerCount is 0.
175    */
176   struct MaskLayerImageKeyRef {
~MaskLayerImageKeyRefMaskLayerImageKeyRef177     ~MaskLayerImageKeyRef() {
178       if (mRawPtr) {
179         mRawPtr->DecLayerCount();
180       }
181     }
182 
MaskLayerImageKeyRefMaskLayerImageKeyRef183     MaskLayerImageKeyRef() : mRawPtr(nullptr) {}
184     MaskLayerImageKeyRef(const MaskLayerImageKeyRef&) = delete;
185     void operator=(const MaskLayerImageKeyRef&) = delete;
186 
ResetMaskLayerImageKeyRef187     void Reset(const MaskLayerImageKey* aPtr) {
188       MOZ_ASSERT(
189           aPtr, "Cannot initialize a MaskLayerImageKeyRef with a null pointer");
190       aPtr->IncLayerCount();
191       if (mRawPtr) {
192         mRawPtr->DecLayerCount();
193       }
194       mRawPtr = aPtr;
195     }
196 
197    private:
198     const MaskLayerImageKey* mRawPtr;
199   };
200 
201   // Find an image container for aKey, returns nullptr if there is no suitable
202   // cached image. If there is an image, then aKey is set to point at the stored
203   // key for the image.
204   ImageContainer* FindImageFor(const MaskLayerImageKey** aKey);
205 
206   // Add an image container with a key to the cache
207   // The image container used will be set as the container in aKey and aKey
208   // itself will be linked from this cache
209   void PutImage(const MaskLayerImageKey* aKey, ImageContainer* aContainer);
210 
211   // Sweep the cache for old image containers that can be deleted
212   void Sweep();
213 
214  protected:
215   class MaskLayerImageEntry : public PLDHashEntryHdr {
216    public:
217     typedef const MaskLayerImageKey& KeyType;
218     typedef const MaskLayerImageKey* KeyTypePointer;
219 
MaskLayerImageEntry(KeyTypePointer aKey)220     explicit MaskLayerImageEntry(KeyTypePointer aKey) : mKey(aKey) {
221       MOZ_COUNT_CTOR(MaskLayerImageEntry);
222     }
MaskLayerImageEntry(const MaskLayerImageEntry & aOther)223     MaskLayerImageEntry(const MaskLayerImageEntry& aOther)
224         : mKey(aOther.mKey.get()) {
225       NS_ERROR("ALLOW_MEMMOVE == true, should never be called");
226     }
~MaskLayerImageEntry()227     ~MaskLayerImageEntry() { MOZ_COUNT_DTOR(MaskLayerImageEntry); }
228 
229     // KeyEquals(): does this entry match this key?
KeyEquals(KeyTypePointer aKey)230     bool KeyEquals(KeyTypePointer aKey) const { return *mKey == *aKey; }
231 
232     // KeyToPointer(): Convert KeyType to KeyTypePointer
KeyToPointer(KeyType aKey)233     static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
234 
235     // HashKey(): calculate the hash number
HashKey(KeyTypePointer aKey)236     static PLDHashNumber HashKey(KeyTypePointer aKey) { return aKey->Hash(); }
237 
238     // ALLOW_MEMMOVE can we move this class with memmove(), or do we have
239     // to use the copy constructor?
240     enum { ALLOW_MEMMOVE = true };
241 
242     bool operator==(const MaskLayerImageEntry& aOther) const {
243       return KeyEquals(aOther.mKey);
244     }
245 
246     nsAutoPtr<const MaskLayerImageKey> mKey;
247     RefPtr<ImageContainer> mContainer;
248   };
249 
250   nsTHashtable<MaskLayerImageEntry> mMaskImageContainers;
251 };
252 
253 }  // namespace mozilla
254 
255 #endif
256