1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef ROTATEDBUFFER_H_
7 #define ROTATEDBUFFER_H_
8 
9 #include "gfxTypes.h"
10 #include <stdint.h>                     // for uint32_t
11 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
12 #include "mozilla/RefPtr.h"             // for RefPtr, already_AddRefed
13 #include "mozilla/gfx/2D.h"             // for DrawTarget, etc
14 #include "mozilla/mozalloc.h"           // for operator delete
15 #include "nsCOMPtr.h"                   // for already_AddRefed
16 #include "nsDebug.h"                    // for NS_RUNTIMEABORT
17 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
18 #include "nsRegion.h"                   // for nsIntRegion
19 #include "LayersTypes.h"
20 
21 namespace mozilla {
22 namespace gfx {
23 class Matrix;
24 } // namespace gfx
25 
26 namespace layers {
27 
28 class TextureClient;
29 class PaintedLayer;
30 
31 /**
32  * This is a cairo/Thebes surface, but with a literal twist. Scrolling
33  * causes the layer's visible region to move. We want to keep
34  * reusing the same surface if the region size hasn't changed, but we don't
35  * want to keep moving the contents of the surface around in memory. So
36  * we use a trick.
37  * Consider just the vertical case, and suppose the buffer is H pixels
38  * high and we're scrolling down by N pixels. Instead of copying the
39  * buffer contents up by N pixels, we leave the buffer contents in place,
40  * and paint content rows H to H+N-1 into rows 0 to N-1 of the buffer.
41  * Then we can refresh the screen by painting rows N to H-1 of the buffer
42  * at row 0 on the screen, and then painting rows 0 to N-1 of the buffer
43  * at row H-N on the screen.
44  * mBufferRotation.y would be N in this example.
45  */
46 class RotatedBuffer {
47 public:
48   typedef gfxContentType ContentType;
49 
RotatedBuffer(const gfx::IntRect & aBufferRect,const gfx::IntPoint & aBufferRotation)50   RotatedBuffer(const gfx::IntRect& aBufferRect,
51                 const gfx::IntPoint& aBufferRotation)
52     : mBufferRect(aBufferRect)
53     , mBufferRotation(aBufferRotation)
54     , mDidSelfCopy(false)
55   { }
RotatedBuffer()56   RotatedBuffer()
57     : mDidSelfCopy(false)
58   { }
59 
60   /*
61    * Which buffer should be drawn to/read from.
62    */
63   enum ContextSource {
64     BUFFER_BLACK, // The normal buffer, or buffer with black background when using component alpha.
65     BUFFER_WHITE, // The buffer with white background, only valid with component alpha.
66     BUFFER_BOTH // The combined black/white buffers, only valid for writing operations, not reading.
67   };
68   // It is the callers repsonsibility to ensure aTarget is flushed after calling
69   // this method.
70   void DrawBufferWithRotation(gfx::DrawTarget* aTarget, ContextSource aSource,
71                               float aOpacity = 1.0,
72                               gfx::CompositionOp aOperator = gfx::CompositionOp::OP_OVER,
73                               gfx::SourceSurface* aMask = nullptr,
74                               const gfx::Matrix* aMaskTransform = nullptr) const;
75 
76   /**
77    * |BufferRect()| is the rect of device pixels that this
78    * RotatedBuffer covers.  That is what DrawBufferWithRotation()
79    * will paint when it's called.
80    */
BufferRect()81   const gfx::IntRect& BufferRect() const { return mBufferRect; }
BufferRotation()82   const gfx::IntPoint& BufferRotation() const { return mBufferRotation; }
83 
84   virtual bool HaveBuffer() const = 0;
85   virtual bool HaveBufferOnWhite() const = 0;
86 
87   virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const = 0;
88 
89 protected:
90 
91   enum XSide {
92     LEFT, RIGHT
93   };
94   enum YSide {
95     TOP, BOTTOM
96   };
97   gfx::IntRect GetQuadrantRectangle(XSide aXSide, YSide aYSide) const;
98 
99   gfx::Rect GetSourceRectangle(XSide aXSide, YSide aYSide) const;
100 
101   /*
102    * If aMask is non-null, then it is used as an alpha mask for rendering this
103    * buffer. aMaskTransform must be non-null if aMask is non-null, and is used
104    * to adjust the coordinate space of the mask.
105    */
106   void DrawBufferQuadrant(gfx::DrawTarget* aTarget, XSide aXSide, YSide aYSide,
107                           ContextSource aSource,
108                           float aOpacity,
109                           gfx::CompositionOp aOperator,
110                           gfx::SourceSurface* aMask,
111                           const gfx::Matrix* aMaskTransform) const;
112 
113   /** The area of the PaintedLayer that is covered by the buffer as a whole */
114   gfx::IntRect             mBufferRect;
115   /**
116    * The x and y rotation of the buffer. Conceptually the buffer
117    * has its origin translated to mBufferRect.TopLeft() - mBufferRotation,
118    * is tiled to fill the plane, and the result is clipped to mBufferRect.
119    * So the pixel at mBufferRotation within the buffer is what gets painted at
120    * mBufferRect.TopLeft().
121    * This is "rotation" in the sense of rotating items in a linear buffer,
122    * where items falling off the end of the buffer are returned to the
123    * buffer at the other end, not 2D rotation!
124    */
125   gfx::IntPoint            mBufferRotation;
126   // When this is true it means that all pixels have moved inside the buffer.
127   // It's not possible to sync with another buffer without a full copy.
128   bool                  mDidSelfCopy;
129 };
130 
131 class SourceRotatedBuffer : public RotatedBuffer
132 {
133 public:
SourceRotatedBuffer(gfx::SourceSurface * aSource,gfx::SourceSurface * aSourceOnWhite,const gfx::IntRect & aBufferRect,const gfx::IntPoint & aBufferRotation)134   SourceRotatedBuffer(gfx::SourceSurface* aSource, gfx::SourceSurface* aSourceOnWhite,
135                       const gfx::IntRect& aBufferRect,
136                       const gfx::IntPoint& aBufferRotation)
137     : RotatedBuffer(aBufferRect, aBufferRotation)
138     , mSource(aSource)
139     , mSourceOnWhite(aSourceOnWhite)
140   { }
141 
142   virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const;
143 
HaveBuffer()144   virtual bool HaveBuffer() const { return !!mSource; }
HaveBufferOnWhite()145   virtual bool HaveBufferOnWhite() const { return !!mSourceOnWhite; }
146 
147 private:
148   RefPtr<gfx::SourceSurface> mSource;
149   RefPtr<gfx::SourceSurface> mSourceOnWhite;
150 };
151 
152 // Mixin class for classes which need logic for loaning out a draw target.
153 // See comments on BorrowDrawTargetForQuadrantUpdate.
154 class BorrowDrawTarget
155 {
156 protected:
157   void ReturnDrawTarget(gfx::DrawTarget*& aReturned);
158 
159   // The draw target loaned by BorrowDrawTargetForQuadrantUpdate. It should not
160   // be used, we just keep a reference to ensure it is kept alive and so we can
161   // correctly restore state when it is returned.
162   RefPtr<gfx::DrawTarget> mLoanedDrawTarget;
163   gfx::Matrix mLoanedTransform;
164 };
165 
166 /**
167  * This class encapsulates the buffer used to retain PaintedLayer contents,
168  * i.e., the contents of the layer's GetVisibleRegion().
169  */
170 class RotatedContentBuffer : public RotatedBuffer
171                            , public BorrowDrawTarget
172 {
173 public:
174   typedef gfxContentType ContentType;
175 
176   /**
177    * Controls the size of the backing buffer of this.
178    * - SizedToVisibleBounds: the backing buffer is exactly the same
179    *   size as the bounds of PaintedLayer's visible region
180    * - ContainsVisibleBounds: the backing buffer is large enough to
181    *   fit visible bounds.  May be larger.
182    */
183   enum BufferSizePolicy {
184     SizedToVisibleBounds,
185     ContainsVisibleBounds
186   };
187 
RotatedContentBuffer(BufferSizePolicy aBufferSizePolicy)188   explicit RotatedContentBuffer(BufferSizePolicy aBufferSizePolicy)
189     : mBufferProvider(nullptr)
190     , mBufferProviderOnWhite(nullptr)
191     , mBufferSizePolicy(aBufferSizePolicy)
192   {
193     MOZ_COUNT_CTOR(RotatedContentBuffer);
194   }
~RotatedContentBuffer()195   virtual ~RotatedContentBuffer()
196   {
197     MOZ_COUNT_DTOR(RotatedContentBuffer);
198   }
199 
200   /**
201    * Wipe out all retained contents. Call this when the entire
202    * buffer becomes invalid.
203    */
Clear()204   void Clear()
205   {
206     mDTBuffer = nullptr;
207     mDTBufferOnWhite = nullptr;
208     mBufferProvider = nullptr;
209     mBufferProviderOnWhite = nullptr;
210     mBufferRect.SetEmpty();
211   }
212 
213   /**
214    * This is returned by BeginPaint. The caller should draw into mTarget.
215    * mRegionToDraw must be drawn. mRegionToInvalidate has been invalidated
216    * by RotatedContentBuffer and must be redrawn on the screen.
217    * mRegionToInvalidate is set when the buffer has changed from
218    * opaque to transparent or vice versa, since the details of rendering can
219    * depend on the buffer type.  mDidSelfCopy is true if we kept our buffer
220    * but used MovePixels() to shift its content.
221    */
222   struct PaintState {
PaintStatePaintState223     PaintState()
224       : mRegionToDraw()
225       , mRegionToInvalidate()
226       , mMode(SurfaceMode::SURFACE_NONE)
227       , mClip(DrawRegionClip::NONE)
228       , mContentType(gfxContentType::SENTINEL)
229       , mDidSelfCopy(false)
230     {}
231 
232     nsIntRegion mRegionToDraw;
233     nsIntRegion mRegionToInvalidate;
234     SurfaceMode mMode;
235     DrawRegionClip mClip;
236     ContentType mContentType;
237     bool mDidSelfCopy;
238   };
239 
240   enum {
241     PAINT_WILL_RESAMPLE = 0x01,
242     PAINT_NO_ROTATION = 0x02,
243     PAINT_CAN_DRAW_ROTATED = 0x04
244   };
245   /**
246    * Start a drawing operation. This returns a PaintState describing what
247    * needs to be drawn to bring the buffer up to date in the visible region.
248    * This queries aLayer to get the currently valid and visible regions.
249    * The returned mTarget may be null if mRegionToDraw is empty.
250    * Otherwise it must not be null.
251    * mRegionToInvalidate will contain mRegionToDraw.
252    * @param aFlags when PAINT_WILL_RESAMPLE is passed, this indicates that
253    * buffer will be resampled when rendering (i.e the effective transform
254    * combined with the scale for the resolution is not just an integer
255    * translation). This will disable buffer rotation (since we don't want
256    * to resample across the rotation boundary) and will ensure that we
257    * make the entire buffer contents valid (since we don't want to sample
258    * invalid pixels outside the visible region, if the visible region doesn't
259    * fill the buffer bounds).
260    * PAINT_CAN_DRAW_ROTATED can be passed if the caller supports drawing
261    * rotated content that crosses the physical buffer boundary. The caller
262    * will need to call BorrowDrawTargetForPainting multiple times to achieve
263    * this.
264    */
265   PaintState BeginPaint(PaintedLayer* aLayer,
266                         uint32_t aFlags);
267 
268   struct DrawIterator {
269     friend class RotatedContentBuffer;
DrawIteratorDrawIterator270     DrawIterator()
271       : mCount(0)
272     {}
273 
274     nsIntRegion mDrawRegion;
275 
276   private:
277     uint32_t mCount;
278   };
279 
280   /**
281    * Fetch a DrawTarget for rendering. The DrawTarget remains owned by
282    * this. See notes on BorrowDrawTargetForQuadrantUpdate.
283    * May return null. If the return value is non-null, it must be
284    * 'un-borrowed' using ReturnDrawTarget.
285    *
286    * If PAINT_CAN_DRAW_ROTATED was specified for BeginPaint, then the caller
287    * must call this function repeatedly (with an iterator) until it returns
288    * nullptr. The caller should draw the mDrawRegion of the iterator instead
289    * of mRegionToDraw in the PaintState.
290    *
291    * @param aPaintState Paint state data returned by a call to BeginPaint
292    * @param aIter Paint state iterator. Only required if PAINT_CAN_DRAW_ROTATED
293    * was specified to BeginPaint.
294    */
295   gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
296                                                DrawIterator* aIter = nullptr);
297 
298   enum {
299     BUFFER_COMPONENT_ALPHA = 0x02 // Dual buffers should be created for drawing with
300                                   // component alpha.
301   };
302   /**
303    * Return a new surface of |aSize| and |aType|.
304    *
305    * If the created buffer supports azure content, then the result(s) will
306    * be returned in aBlackDT/aWhiteDT, otherwise aBlackSurface/aWhiteSurface
307    * will be used.
308    */
309   virtual void
310   CreateBuffer(ContentType aType, const gfx::IntRect& aRect, uint32_t aFlags,
311                RefPtr<gfx::DrawTarget>* aBlackDT, RefPtr<gfx::DrawTarget>* aWhiteDT) = 0;
312 
313   /**
314    * Get the underlying buffer, if any. This is useful because we can pass
315    * in the buffer as the default "reference surface" if there is one.
316    * Don't use it for anything else!
317    */
GetDTBuffer()318   gfx::DrawTarget* GetDTBuffer() { return mDTBuffer; }
GetDTBufferOnWhite()319   gfx::DrawTarget* GetDTBufferOnWhite() { return mDTBufferOnWhite; }
320 
321   virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const;
322 
323   /**
324    * Complete the drawing operation. The region to draw must have been
325    * drawn before this is called. The contents of the buffer are drawn
326    * to aTarget.
327    */
328   void DrawTo(PaintedLayer* aLayer,
329               gfx::DrawTarget* aTarget,
330               float aOpacity,
331               gfx::CompositionOp aOp,
332               gfx::SourceSurface* aMask,
333               const gfx::Matrix* aMaskTransform);
334 
335 protected:
336   // new texture client versions
SetBufferProvider(TextureClient * aClient)337   void SetBufferProvider(TextureClient* aClient)
338   {
339     // Only this buffer provider can give us a buffer.  If we
340     // already have one, something has gone wrong.
341     MOZ_ASSERT(!aClient || !mDTBuffer || !mDTBuffer->IsValid());
342 
343     mBufferProvider = aClient;
344     if (!mBufferProvider) {
345       mDTBuffer = nullptr;
346     }
347   }
348 
SetBufferProviderOnWhite(TextureClient * aClient)349   void SetBufferProviderOnWhite(TextureClient* aClient)
350   {
351     // Only this buffer provider can give us a buffer.  If we
352     // already have one, something has gone wrong.
353     MOZ_ASSERT(!aClient || !mDTBufferOnWhite || !mDTBufferOnWhite->IsValid());
354 
355     mBufferProviderOnWhite = aClient;
356     if (!mBufferProviderOnWhite) {
357       mDTBufferOnWhite = nullptr;
358     }
359   }
360 
361   /**
362    * Get a draw target at the specified resolution for updating |aBounds|,
363    * which must be contained within a single quadrant.
364    *
365    * The result should only be held temporarily by the caller (it will be kept
366    * alive by this). Once used it should be returned using ReturnDrawTarget.
367    * BorrowDrawTargetForQuadrantUpdate may not be called more than once without
368    * first calling ReturnDrawTarget.
369    *
370    * ReturnDrawTarget will restore the transform on the draw target. But it is
371    * the callers responsibility to restore the clip. The caller should flush the
372    * draw target, if necessary.
373    */
374   gfx::DrawTarget*
375   BorrowDrawTargetForQuadrantUpdate(const gfx::IntRect& aBounds,
376                                     ContextSource aSource,
377                                     DrawIterator* aIter);
378 
379   static bool IsClippingCheap(gfx::DrawTarget* aTarget, const nsIntRegion& aRegion);
380 
381 protected:
382   /**
383    * Return the buffer's content type.  Requires a valid buffer or
384    * buffer provider.
385    */
386   gfxContentType BufferContentType();
387   bool BufferSizeOkFor(const gfx::IntSize& aSize);
388   /**
389    * If the buffer hasn't been mapped, map it.
390    */
391   bool EnsureBuffer();
392   bool EnsureBufferOnWhite();
393 
394   // Flush our buffers if they are mapped.
395   void FlushBuffers();
396 
397   /**
398    * True if we have a buffer where we can get it (but not necessarily
399    * mapped currently).
400    */
401   virtual bool HaveBuffer() const;
402   virtual bool HaveBufferOnWhite() const;
403 
404   /**
405    * Any actions that should be performed at the last moment before we begin
406    * rendering the next frame. I.e., after we calculate what we will draw,
407    * but before we rotate the buffer and possibly create new buffers.
408    * aRegionToDraw is the region which is guaranteed to be overwritten when
409    * drawing the next frame.
410    */
FinalizeFrame(const nsIntRegion & aRegionToDraw)411   virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) {}
412 
413   RefPtr<gfx::DrawTarget> mDTBuffer;
414   RefPtr<gfx::DrawTarget> mDTBufferOnWhite;
415 
416   /**
417    * These members are only set transiently.  They're used to map mDTBuffer
418    * when we're using surfaces that require explicit map/unmap. Only one
419    * may be used at a time.
420    */
421   TextureClient* mBufferProvider;
422   TextureClient* mBufferProviderOnWhite;
423 
424   BufferSizePolicy      mBufferSizePolicy;
425 };
426 
427 } // namespace layers
428 } // namespace mozilla
429 
430 #endif /* ROTATEDBUFFER_H_ */
431