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