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