1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/layers/RotatedBuffer.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,412 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifndef ROTATEDBUFFER_H_ 1.10 +#define ROTATEDBUFFER_H_ 1.11 + 1.12 +#include "gfxTypes.h" 1.13 +#include <stdint.h> // for uint32_t 1.14 +#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc 1.15 +#include "mozilla/RefPtr.h" // for RefPtr, TemporaryRef 1.16 +#include "mozilla/gfx/2D.h" // for DrawTarget, etc 1.17 +#include "mozilla/mozalloc.h" // for operator delete 1.18 +#include "nsAutoPtr.h" // for nsRefPtr 1.19 +#include "nsCOMPtr.h" // for already_AddRefed 1.20 +#include "nsDebug.h" // for NS_RUNTIMEABORT 1.21 +#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc 1.22 +#include "nsPoint.h" // for nsIntPoint 1.23 +#include "nsRect.h" // for nsIntRect 1.24 +#include "nsRegion.h" // for nsIntRegion 1.25 +#include "LayersTypes.h" 1.26 + 1.27 +struct nsIntSize; 1.28 + 1.29 +namespace mozilla { 1.30 +namespace gfx { 1.31 +class Matrix; 1.32 +} 1.33 + 1.34 +namespace layers { 1.35 + 1.36 +class TextureClient; 1.37 +class ThebesLayer; 1.38 + 1.39 +/** 1.40 + * This is a cairo/Thebes surface, but with a literal twist. Scrolling 1.41 + * causes the layer's visible region to move. We want to keep 1.42 + * reusing the same surface if the region size hasn't changed, but we don't 1.43 + * want to keep moving the contents of the surface around in memory. So 1.44 + * we use a trick. 1.45 + * Consider just the vertical case, and suppose the buffer is H pixels 1.46 + * high and we're scrolling down by N pixels. Instead of copying the 1.47 + * buffer contents up by N pixels, we leave the buffer contents in place, 1.48 + * and paint content rows H to H+N-1 into rows 0 to N-1 of the buffer. 1.49 + * Then we can refresh the screen by painting rows N to H-1 of the buffer 1.50 + * at row 0 on the screen, and then painting rows 0 to N-1 of the buffer 1.51 + * at row H-N on the screen. 1.52 + * mBufferRotation.y would be N in this example. 1.53 + */ 1.54 +class RotatedBuffer { 1.55 +public: 1.56 + typedef gfxContentType ContentType; 1.57 + 1.58 + RotatedBuffer(gfx::DrawTarget* aDTBuffer, gfx::DrawTarget* aDTBufferOnWhite, 1.59 + const nsIntRect& aBufferRect, 1.60 + const nsIntPoint& aBufferRotation) 1.61 + : mDTBuffer(aDTBuffer) 1.62 + , mDTBufferOnWhite(aDTBufferOnWhite) 1.63 + , mBufferRect(aBufferRect) 1.64 + , mBufferRotation(aBufferRotation) 1.65 + , mDidSelfCopy(false) 1.66 + { } 1.67 + RotatedBuffer() 1.68 + : mDidSelfCopy(false) 1.69 + { } 1.70 + 1.71 + /* 1.72 + * Which buffer should be drawn to/read from. 1.73 + */ 1.74 + enum ContextSource { 1.75 + BUFFER_BLACK, // The normal buffer, or buffer with black background when using component alpha. 1.76 + BUFFER_WHITE, // The buffer with white background, only valid with component alpha. 1.77 + BUFFER_BOTH // The combined black/white buffers, only valid for writing operations, not reading. 1.78 + }; 1.79 + // It is the callers repsonsibility to ensure aTarget is flushed after calling 1.80 + // this method. 1.81 + void DrawBufferWithRotation(gfx::DrawTarget* aTarget, ContextSource aSource, 1.82 + float aOpacity = 1.0, 1.83 + gfx::CompositionOp aOperator = gfx::CompositionOp::OP_OVER, 1.84 + gfx::SourceSurface* aMask = nullptr, 1.85 + const gfx::Matrix* aMaskTransform = nullptr) const; 1.86 + 1.87 + /** 1.88 + * |BufferRect()| is the rect of device pixels that this 1.89 + * RotatedBuffer covers. That is what DrawBufferWithRotation() 1.90 + * will paint when it's called. 1.91 + */ 1.92 + const nsIntRect& BufferRect() const { return mBufferRect; } 1.93 + const nsIntPoint& BufferRotation() const { return mBufferRotation; } 1.94 + 1.95 + virtual bool HaveBuffer() const { return mDTBuffer; } 1.96 + virtual bool HaveBufferOnWhite() const { return mDTBufferOnWhite; } 1.97 + 1.98 +protected: 1.99 + 1.100 + enum XSide { 1.101 + LEFT, RIGHT 1.102 + }; 1.103 + enum YSide { 1.104 + TOP, BOTTOM 1.105 + }; 1.106 + nsIntRect GetQuadrantRectangle(XSide aXSide, YSide aYSide) const; 1.107 + 1.108 + gfx::Rect GetSourceRectangle(XSide aXSide, YSide aYSide) const; 1.109 + 1.110 + /* 1.111 + * If aMask is non-null, then it is used as an alpha mask for rendering this 1.112 + * buffer. aMaskTransform must be non-null if aMask is non-null, and is used 1.113 + * to adjust the coordinate space of the mask. 1.114 + */ 1.115 + void DrawBufferQuadrant(gfx::DrawTarget* aTarget, XSide aXSide, YSide aYSide, 1.116 + ContextSource aSource, 1.117 + float aOpacity, 1.118 + gfx::CompositionOp aOperator, 1.119 + gfx::SourceSurface* aMask, 1.120 + const gfx::Matrix* aMaskTransform) const; 1.121 + 1.122 + RefPtr<gfx::DrawTarget> mDTBuffer; 1.123 + RefPtr<gfx::DrawTarget> mDTBufferOnWhite; 1.124 + /** The area of the ThebesLayer that is covered by the buffer as a whole */ 1.125 + nsIntRect mBufferRect; 1.126 + /** 1.127 + * The x and y rotation of the buffer. Conceptually the buffer 1.128 + * has its origin translated to mBufferRect.TopLeft() - mBufferRotation, 1.129 + * is tiled to fill the plane, and the result is clipped to mBufferRect. 1.130 + * So the pixel at mBufferRotation within the buffer is what gets painted at 1.131 + * mBufferRect.TopLeft(). 1.132 + * This is "rotation" in the sense of rotating items in a linear buffer, 1.133 + * where items falling off the end of the buffer are returned to the 1.134 + * buffer at the other end, not 2D rotation! 1.135 + */ 1.136 + nsIntPoint mBufferRotation; 1.137 + // When this is true it means that all pixels have moved inside the buffer. 1.138 + // It's not possible to sync with another buffer without a full copy. 1.139 + bool mDidSelfCopy; 1.140 +}; 1.141 + 1.142 +// Mixin class for classes which need logic for loaning out a draw target. 1.143 +// See comments on BorrowDrawTargetForQuadrantUpdate. 1.144 +class BorrowDrawTarget 1.145 +{ 1.146 +protected: 1.147 + void ReturnDrawTarget(gfx::DrawTarget*& aReturned); 1.148 + 1.149 + // The draw target loaned by BorrowDrawTargetForQuadrantUpdate. It should not 1.150 + // be used, we just keep a reference to ensure it is kept alive and so we can 1.151 + // correctly restore state when it is returned. 1.152 + RefPtr<gfx::DrawTarget> mLoanedDrawTarget; 1.153 + gfx::Matrix mLoanedTransform; 1.154 +}; 1.155 + 1.156 +/** 1.157 + * This class encapsulates the buffer used to retain ThebesLayer contents, 1.158 + * i.e., the contents of the layer's GetVisibleRegion(). 1.159 + */ 1.160 +class RotatedContentBuffer : public RotatedBuffer 1.161 + , public BorrowDrawTarget 1.162 +{ 1.163 +public: 1.164 + typedef gfxContentType ContentType; 1.165 + 1.166 + /** 1.167 + * Controls the size of the backing buffer of this. 1.168 + * - SizedToVisibleBounds: the backing buffer is exactly the same 1.169 + * size as the bounds of ThebesLayer's visible region 1.170 + * - ContainsVisibleBounds: the backing buffer is large enough to 1.171 + * fit visible bounds. May be larger. 1.172 + */ 1.173 + enum BufferSizePolicy { 1.174 + SizedToVisibleBounds, 1.175 + ContainsVisibleBounds 1.176 + }; 1.177 + 1.178 + RotatedContentBuffer(BufferSizePolicy aBufferSizePolicy) 1.179 + : mBufferProvider(nullptr) 1.180 + , mBufferProviderOnWhite(nullptr) 1.181 + , mBufferSizePolicy(aBufferSizePolicy) 1.182 + { 1.183 + MOZ_COUNT_CTOR(RotatedContentBuffer); 1.184 + } 1.185 + virtual ~RotatedContentBuffer() 1.186 + { 1.187 + MOZ_COUNT_DTOR(RotatedContentBuffer); 1.188 + } 1.189 + 1.190 + /** 1.191 + * Wipe out all retained contents. Call this when the entire 1.192 + * buffer becomes invalid. 1.193 + */ 1.194 + void Clear() 1.195 + { 1.196 + mDTBuffer = nullptr; 1.197 + mDTBufferOnWhite = nullptr; 1.198 + mBufferProvider = nullptr; 1.199 + mBufferProviderOnWhite = nullptr; 1.200 + mBufferRect.SetEmpty(); 1.201 + } 1.202 + 1.203 + /** 1.204 + * This is returned by BeginPaint. The caller should draw into mTarget. 1.205 + * mRegionToDraw must be drawn. mRegionToInvalidate has been invalidated 1.206 + * by RotatedContentBuffer and must be redrawn on the screen. 1.207 + * mRegionToInvalidate is set when the buffer has changed from 1.208 + * opaque to transparent or vice versa, since the details of rendering can 1.209 + * depend on the buffer type. mDidSelfCopy is true if we kept our buffer 1.210 + * but used MovePixels() to shift its content. 1.211 + */ 1.212 + struct PaintState { 1.213 + PaintState() 1.214 + : mMode(SurfaceMode::SURFACE_NONE) 1.215 + , mContentType(gfxContentType::SENTINEL) 1.216 + , mDidSelfCopy(false) 1.217 + {} 1.218 + 1.219 + nsIntRegion mRegionToDraw; 1.220 + nsIntRegion mRegionToInvalidate; 1.221 + SurfaceMode mMode; 1.222 + DrawRegionClip mClip; 1.223 + ContentType mContentType; 1.224 + bool mDidSelfCopy; 1.225 + }; 1.226 + 1.227 + enum { 1.228 + PAINT_WILL_RESAMPLE = 0x01, 1.229 + PAINT_NO_ROTATION = 0x02, 1.230 + PAINT_CAN_DRAW_ROTATED = 0x04 1.231 + }; 1.232 + /** 1.233 + * Start a drawing operation. This returns a PaintState describing what 1.234 + * needs to be drawn to bring the buffer up to date in the visible region. 1.235 + * This queries aLayer to get the currently valid and visible regions. 1.236 + * The returned mTarget may be null if mRegionToDraw is empty. 1.237 + * Otherwise it must not be null. 1.238 + * mRegionToInvalidate will contain mRegionToDraw. 1.239 + * @param aFlags when PAINT_WILL_RESAMPLE is passed, this indicates that 1.240 + * buffer will be resampled when rendering (i.e the effective transform 1.241 + * combined with the scale for the resolution is not just an integer 1.242 + * translation). This will disable buffer rotation (since we don't want 1.243 + * to resample across the rotation boundary) and will ensure that we 1.244 + * make the entire buffer contents valid (since we don't want to sample 1.245 + * invalid pixels outside the visible region, if the visible region doesn't 1.246 + * fill the buffer bounds). 1.247 + * PAINT_CAN_DRAW_ROTATED can be passed if the caller supports drawing 1.248 + * rotated content that crosses the physical buffer boundary. The caller 1.249 + * will need to call BorrowDrawTargetForPainting multiple times to achieve 1.250 + * this. 1.251 + */ 1.252 + PaintState BeginPaint(ThebesLayer* aLayer, 1.253 + uint32_t aFlags); 1.254 + 1.255 + struct DrawIterator { 1.256 + friend class RotatedContentBuffer; 1.257 + friend class ContentClientIncremental; 1.258 + DrawIterator() 1.259 + : mCount(0) 1.260 + {} 1.261 + 1.262 + nsIntRegion mDrawRegion; 1.263 + 1.264 + private: 1.265 + uint32_t mCount; 1.266 + }; 1.267 + 1.268 + /** 1.269 + * Fetch a DrawTarget for rendering. The DrawTarget remains owned by 1.270 + * this. See notes on BorrowDrawTargetForQuadrantUpdate. 1.271 + * May return null. If the return value is non-null, it must be 1.272 + * 'un-borrowed' using ReturnDrawTarget. 1.273 + * 1.274 + * If PAINT_CAN_DRAW_ROTATED was specified for BeginPaint, then the caller 1.275 + * must call this function repeatedly (with an iterator) until it returns 1.276 + * nullptr. The caller should draw the mDrawRegion of the iterator instead 1.277 + * of mRegionToDraw in the PaintState. 1.278 + * 1.279 + * @param aPaintState Paint state data returned by a call to BeginPaint 1.280 + * @param aIter Paint state iterator. Only required if PAINT_CAN_DRAW_ROTATED 1.281 + * was specified to BeginPaint. 1.282 + */ 1.283 + gfx::DrawTarget* BorrowDrawTargetForPainting(const PaintState& aPaintState, 1.284 + DrawIterator* aIter = nullptr); 1.285 + 1.286 + enum { 1.287 + ALLOW_REPEAT = 0x01, 1.288 + BUFFER_COMPONENT_ALPHA = 0x02 // Dual buffers should be created for drawing with 1.289 + // component alpha. 1.290 + }; 1.291 + /** 1.292 + * Return a new surface of |aSize| and |aType|. 1.293 + * @param aFlags if ALLOW_REPEAT is set, then the buffer should be configured 1.294 + * to allow repeat-mode, otherwise it should be in pad (clamp) mode 1.295 + * If the created buffer supports azure content, then the result(s) will 1.296 + * be returned in aBlackDT/aWhiteDT, otherwise aBlackSurface/aWhiteSurface 1.297 + * will be used. 1.298 + */ 1.299 + virtual void 1.300 + CreateBuffer(ContentType aType, const nsIntRect& aRect, uint32_t aFlags, 1.301 + RefPtr<gfx::DrawTarget>* aBlackDT, RefPtr<gfx::DrawTarget>* aWhiteDT) = 0; 1.302 + 1.303 + /** 1.304 + * Get the underlying buffer, if any. This is useful because we can pass 1.305 + * in the buffer as the default "reference surface" if there is one. 1.306 + * Don't use it for anything else! 1.307 + */ 1.308 + gfx::DrawTarget* GetDTBuffer() { return mDTBuffer; } 1.309 + gfx::DrawTarget* GetDTBufferOnWhite() { return mDTBufferOnWhite; } 1.310 + 1.311 + /** 1.312 + * Complete the drawing operation. The region to draw must have been 1.313 + * drawn before this is called. The contents of the buffer are drawn 1.314 + * to aTarget. 1.315 + */ 1.316 + void DrawTo(ThebesLayer* aLayer, 1.317 + gfx::DrawTarget* aTarget, 1.318 + float aOpacity, 1.319 + gfx::CompositionOp aOp, 1.320 + gfx::SourceSurface* aMask, 1.321 + const gfx::Matrix* aMaskTransform); 1.322 + 1.323 +protected: 1.324 + // new texture client versions 1.325 + void SetBufferProvider(TextureClient* aClient) 1.326 + { 1.327 + // Only this buffer provider can give us a buffer. If we 1.328 + // already have one, something has gone wrong. 1.329 + MOZ_ASSERT(!aClient || !mDTBuffer); 1.330 + 1.331 + mBufferProvider = aClient; 1.332 + if (!mBufferProvider) { 1.333 + mDTBuffer = nullptr; 1.334 + } 1.335 + } 1.336 + 1.337 + void SetBufferProviderOnWhite(TextureClient* aClient) 1.338 + { 1.339 + // Only this buffer provider can give us a buffer. If we 1.340 + // already have one, something has gone wrong. 1.341 + MOZ_ASSERT(!aClient || !mDTBufferOnWhite); 1.342 + 1.343 + mBufferProviderOnWhite = aClient; 1.344 + if (!mBufferProviderOnWhite) { 1.345 + mDTBufferOnWhite = nullptr; 1.346 + } 1.347 + } 1.348 + 1.349 + /** 1.350 + * Get a draw target at the specified resolution for updating |aBounds|, 1.351 + * which must be contained within a single quadrant. 1.352 + * 1.353 + * The result should only be held temporarily by the caller (it will be kept 1.354 + * alive by this). Once used it should be returned using ReturnDrawTarget. 1.355 + * BorrowDrawTargetForQuadrantUpdate may not be called more than once without 1.356 + * first calling ReturnDrawTarget. 1.357 + * 1.358 + * ReturnDrawTarget will restore the transform on the draw target. But it is 1.359 + * the callers responsibility to restore the clip. The caller should flush the 1.360 + * draw target, if necessary. 1.361 + */ 1.362 + gfx::DrawTarget* 1.363 + BorrowDrawTargetForQuadrantUpdate(const nsIntRect& aBounds, 1.364 + ContextSource aSource, 1.365 + DrawIterator* aIter); 1.366 + 1.367 + static bool IsClippingCheap(gfx::DrawTarget* aTarget, const nsIntRegion& aRegion); 1.368 + 1.369 +protected: 1.370 + /** 1.371 + * Return the buffer's content type. Requires a valid buffer or 1.372 + * buffer provider. 1.373 + */ 1.374 + gfxContentType BufferContentType(); 1.375 + bool BufferSizeOkFor(const nsIntSize& aSize); 1.376 + /** 1.377 + * If the buffer hasn't been mapped, map it. 1.378 + */ 1.379 + bool EnsureBuffer(); 1.380 + bool EnsureBufferOnWhite(); 1.381 + 1.382 + // Flush our buffers if they are mapped. 1.383 + void FlushBuffers(); 1.384 + 1.385 + /** 1.386 + * True if we have a buffer where we can get it (but not necessarily 1.387 + * mapped currently). 1.388 + */ 1.389 + virtual bool HaveBuffer() const; 1.390 + virtual bool HaveBufferOnWhite() const; 1.391 + 1.392 + /** 1.393 + * Any actions that should be performed at the last moment before we begin 1.394 + * rendering the next frame. I.e., after we calculate what we will draw, 1.395 + * but before we rotate the buffer and possibly create new buffers. 1.396 + * aRegionToDraw is the region which is guaranteed to be overwritten when 1.397 + * drawing the next frame. 1.398 + */ 1.399 + virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) {} 1.400 + 1.401 + /** 1.402 + * These members are only set transiently. They're used to map mDTBuffer 1.403 + * when we're using surfaces that require explicit map/unmap. Only one 1.404 + * may be used at a time. 1.405 + */ 1.406 + TextureClient* mBufferProvider; 1.407 + TextureClient* mBufferProviderOnWhite; 1.408 + 1.409 + BufferSizePolicy mBufferSizePolicy; 1.410 +}; 1.411 + 1.412 +} 1.413 +} 1.414 + 1.415 +#endif /* ROTATEDBUFFER_H_ */