gfx/layers/RotatedBuffer.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial