michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef ROTATEDBUFFER_H_ michael@0: #define ROTATEDBUFFER_H_ michael@0: michael@0: #include "gfxTypes.h" michael@0: #include // for uint32_t michael@0: #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc michael@0: #include "mozilla/RefPtr.h" // for RefPtr, TemporaryRef michael@0: #include "mozilla/gfx/2D.h" // for DrawTarget, etc michael@0: #include "mozilla/mozalloc.h" // for operator delete michael@0: #include "nsAutoPtr.h" // for nsRefPtr michael@0: #include "nsCOMPtr.h" // for already_AddRefed michael@0: #include "nsDebug.h" // for NS_RUNTIMEABORT michael@0: #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc michael@0: #include "nsPoint.h" // for nsIntPoint michael@0: #include "nsRect.h" // for nsIntRect michael@0: #include "nsRegion.h" // for nsIntRegion michael@0: #include "LayersTypes.h" michael@0: michael@0: struct nsIntSize; michael@0: michael@0: namespace mozilla { michael@0: namespace gfx { michael@0: class Matrix; michael@0: } michael@0: michael@0: namespace layers { michael@0: michael@0: class TextureClient; michael@0: class ThebesLayer; michael@0: michael@0: /** michael@0: * This is a cairo/Thebes surface, but with a literal twist. Scrolling michael@0: * causes the layer's visible region to move. We want to keep michael@0: * reusing the same surface if the region size hasn't changed, but we don't michael@0: * want to keep moving the contents of the surface around in memory. So michael@0: * we use a trick. michael@0: * Consider just the vertical case, and suppose the buffer is H pixels michael@0: * high and we're scrolling down by N pixels. Instead of copying the michael@0: * buffer contents up by N pixels, we leave the buffer contents in place, michael@0: * and paint content rows H to H+N-1 into rows 0 to N-1 of the buffer. michael@0: * Then we can refresh the screen by painting rows N to H-1 of the buffer michael@0: * at row 0 on the screen, and then painting rows 0 to N-1 of the buffer michael@0: * at row H-N on the screen. michael@0: * mBufferRotation.y would be N in this example. michael@0: */ michael@0: class RotatedBuffer { michael@0: public: michael@0: typedef gfxContentType ContentType; michael@0: michael@0: RotatedBuffer(gfx::DrawTarget* aDTBuffer, gfx::DrawTarget* aDTBufferOnWhite, michael@0: const nsIntRect& aBufferRect, michael@0: const nsIntPoint& aBufferRotation) michael@0: : mDTBuffer(aDTBuffer) michael@0: , mDTBufferOnWhite(aDTBufferOnWhite) michael@0: , mBufferRect(aBufferRect) michael@0: , mBufferRotation(aBufferRotation) michael@0: , mDidSelfCopy(false) michael@0: { } michael@0: RotatedBuffer() michael@0: : mDidSelfCopy(false) michael@0: { } michael@0: michael@0: /* michael@0: * Which buffer should be drawn to/read from. michael@0: */ michael@0: enum ContextSource { michael@0: BUFFER_BLACK, // The normal buffer, or buffer with black background when using component alpha. michael@0: BUFFER_WHITE, // The buffer with white background, only valid with component alpha. michael@0: BUFFER_BOTH // The combined black/white buffers, only valid for writing operations, not reading. michael@0: }; michael@0: // It is the callers repsonsibility to ensure aTarget is flushed after calling michael@0: // this method. michael@0: void DrawBufferWithRotation(gfx::DrawTarget* aTarget, ContextSource aSource, michael@0: float aOpacity = 1.0, michael@0: gfx::CompositionOp aOperator = gfx::CompositionOp::OP_OVER, michael@0: gfx::SourceSurface* aMask = nullptr, michael@0: const gfx::Matrix* aMaskTransform = nullptr) const; michael@0: michael@0: /** michael@0: * |BufferRect()| is the rect of device pixels that this michael@0: * RotatedBuffer covers. That is what DrawBufferWithRotation() michael@0: * will paint when it's called. michael@0: */ michael@0: const nsIntRect& BufferRect() const { return mBufferRect; } michael@0: const nsIntPoint& BufferRotation() const { return mBufferRotation; } michael@0: michael@0: virtual bool HaveBuffer() const { return mDTBuffer; } michael@0: virtual bool HaveBufferOnWhite() const { return mDTBufferOnWhite; } michael@0: michael@0: protected: michael@0: michael@0: enum XSide { michael@0: LEFT, RIGHT michael@0: }; michael@0: enum YSide { michael@0: TOP, BOTTOM michael@0: }; michael@0: nsIntRect GetQuadrantRectangle(XSide aXSide, YSide aYSide) const; michael@0: michael@0: gfx::Rect GetSourceRectangle(XSide aXSide, YSide aYSide) const; michael@0: michael@0: /* michael@0: * If aMask is non-null, then it is used as an alpha mask for rendering this michael@0: * buffer. aMaskTransform must be non-null if aMask is non-null, and is used michael@0: * to adjust the coordinate space of the mask. michael@0: */ michael@0: void DrawBufferQuadrant(gfx::DrawTarget* aTarget, XSide aXSide, YSide aYSide, michael@0: ContextSource aSource, michael@0: float aOpacity, michael@0: gfx::CompositionOp aOperator, michael@0: gfx::SourceSurface* aMask, michael@0: const gfx::Matrix* aMaskTransform) const; michael@0: michael@0: RefPtr mDTBuffer; michael@0: RefPtr mDTBufferOnWhite; michael@0: /** The area of the ThebesLayer that is covered by the buffer as a whole */ michael@0: nsIntRect mBufferRect; michael@0: /** michael@0: * The x and y rotation of the buffer. Conceptually the buffer michael@0: * has its origin translated to mBufferRect.TopLeft() - mBufferRotation, michael@0: * is tiled to fill the plane, and the result is clipped to mBufferRect. michael@0: * So the pixel at mBufferRotation within the buffer is what gets painted at michael@0: * mBufferRect.TopLeft(). michael@0: * This is "rotation" in the sense of rotating items in a linear buffer, michael@0: * where items falling off the end of the buffer are returned to the michael@0: * buffer at the other end, not 2D rotation! michael@0: */ michael@0: nsIntPoint mBufferRotation; michael@0: // When this is true it means that all pixels have moved inside the buffer. michael@0: // It's not possible to sync with another buffer without a full copy. michael@0: bool mDidSelfCopy; michael@0: }; michael@0: michael@0: // Mixin class for classes which need logic for loaning out a draw target. michael@0: // See comments on BorrowDrawTargetForQuadrantUpdate. michael@0: class BorrowDrawTarget michael@0: { michael@0: protected: michael@0: void ReturnDrawTarget(gfx::DrawTarget*& aReturned); michael@0: michael@0: // The draw target loaned by BorrowDrawTargetForQuadrantUpdate. It should not michael@0: // be used, we just keep a reference to ensure it is kept alive and so we can michael@0: // correctly restore state when it is returned. michael@0: RefPtr mLoanedDrawTarget; michael@0: gfx::Matrix mLoanedTransform; michael@0: }; michael@0: michael@0: /** michael@0: * This class encapsulates the buffer used to retain ThebesLayer contents, michael@0: * i.e., the contents of the layer's GetVisibleRegion(). michael@0: */ michael@0: class RotatedContentBuffer : public RotatedBuffer michael@0: , public BorrowDrawTarget michael@0: { michael@0: public: michael@0: typedef gfxContentType ContentType; michael@0: michael@0: /** michael@0: * Controls the size of the backing buffer of this. michael@0: * - SizedToVisibleBounds: the backing buffer is exactly the same michael@0: * size as the bounds of ThebesLayer's visible region michael@0: * - ContainsVisibleBounds: the backing buffer is large enough to michael@0: * fit visible bounds. May be larger. michael@0: */ michael@0: enum BufferSizePolicy { michael@0: SizedToVisibleBounds, michael@0: ContainsVisibleBounds michael@0: }; michael@0: michael@0: RotatedContentBuffer(BufferSizePolicy aBufferSizePolicy) michael@0: : mBufferProvider(nullptr) michael@0: , mBufferProviderOnWhite(nullptr) michael@0: , mBufferSizePolicy(aBufferSizePolicy) michael@0: { michael@0: MOZ_COUNT_CTOR(RotatedContentBuffer); michael@0: } michael@0: virtual ~RotatedContentBuffer() michael@0: { michael@0: MOZ_COUNT_DTOR(RotatedContentBuffer); michael@0: } michael@0: michael@0: /** michael@0: * Wipe out all retained contents. Call this when the entire michael@0: * buffer becomes invalid. michael@0: */ michael@0: void Clear() michael@0: { michael@0: mDTBuffer = nullptr; michael@0: mDTBufferOnWhite = nullptr; michael@0: mBufferProvider = nullptr; michael@0: mBufferProviderOnWhite = nullptr; michael@0: mBufferRect.SetEmpty(); michael@0: } michael@0: michael@0: /** michael@0: * This is returned by BeginPaint. The caller should draw into mTarget. michael@0: * mRegionToDraw must be drawn. mRegionToInvalidate has been invalidated michael@0: * by RotatedContentBuffer and must be redrawn on the screen. michael@0: * mRegionToInvalidate is set when the buffer has changed from michael@0: * opaque to transparent or vice versa, since the details of rendering can michael@0: * depend on the buffer type. mDidSelfCopy is true if we kept our buffer michael@0: * but used MovePixels() to shift its content. michael@0: */ michael@0: struct PaintState { michael@0: PaintState() michael@0: : mMode(SurfaceMode::SURFACE_NONE) michael@0: , mContentType(gfxContentType::SENTINEL) michael@0: , mDidSelfCopy(false) michael@0: {} michael@0: michael@0: nsIntRegion mRegionToDraw; michael@0: nsIntRegion mRegionToInvalidate; michael@0: SurfaceMode mMode; michael@0: DrawRegionClip mClip; michael@0: ContentType mContentType; michael@0: bool mDidSelfCopy; michael@0: }; michael@0: michael@0: enum { michael@0: PAINT_WILL_RESAMPLE = 0x01, michael@0: PAINT_NO_ROTATION = 0x02, michael@0: PAINT_CAN_DRAW_ROTATED = 0x04 michael@0: }; michael@0: /** michael@0: * Start a drawing operation. This returns a PaintState describing what michael@0: * needs to be drawn to bring the buffer up to date in the visible region. michael@0: * This queries aLayer to get the currently valid and visible regions. michael@0: * The returned mTarget may be null if mRegionToDraw is empty. michael@0: * Otherwise it must not be null. michael@0: * mRegionToInvalidate will contain mRegionToDraw. michael@0: * @param aFlags when PAINT_WILL_RESAMPLE is passed, this indicates that michael@0: * buffer will be resampled when rendering (i.e the effective transform michael@0: * combined with the scale for the resolution is not just an integer michael@0: * translation). This will disable buffer rotation (since we don't want michael@0: * to resample across the rotation boundary) and will ensure that we michael@0: * make the entire buffer contents valid (since we don't want to sample michael@0: * invalid pixels outside the visible region, if the visible region doesn't michael@0: * fill the buffer bounds). michael@0: * PAINT_CAN_DRAW_ROTATED can be passed if the caller supports drawing michael@0: * rotated content that crosses the physical buffer boundary. The caller michael@0: * will need to call BorrowDrawTargetForPainting multiple times to achieve michael@0: * this. michael@0: */ michael@0: PaintState BeginPaint(ThebesLayer* aLayer, michael@0: uint32_t aFlags); michael@0: michael@0: struct DrawIterator { michael@0: friend class RotatedContentBuffer; michael@0: friend class ContentClientIncremental; michael@0: DrawIterator() michael@0: : mCount(0) michael@0: {} michael@0: michael@0: nsIntRegion mDrawRegion; michael@0: michael@0: private: michael@0: uint32_t mCount; michael@0: }; michael@0: michael@0: /** michael@0: * Fetch a DrawTarget for rendering. The DrawTarget remains owned by michael@0: * this. See notes on BorrowDrawTargetForQuadrantUpdate. michael@0: * May return null. If the return value is non-null, it must be michael@0: * 'un-borrowed' using ReturnDrawTarget. michael@0: * michael@0: * If PAINT_CAN_DRAW_ROTATED was specified for BeginPaint, then the caller michael@0: * must call this function repeatedly (with an iterator) until it returns michael@0: * nullptr. The caller should draw the mDrawRegion of the iterator instead michael@0: * of mRegionToDraw in the PaintState. michael@0: * michael@0: * @param aPaintState Paint state data returned by a call to BeginPaint michael@0: * @param aIter Paint state iterator. Only required if PAINT_CAN_DRAW_ROTATED michael@0: * was specified to BeginPaint. michael@0: */ michael@0: gfx::DrawTarget* BorrowDrawTargetForPainting(const PaintState& aPaintState, michael@0: DrawIterator* aIter = nullptr); michael@0: michael@0: enum { michael@0: ALLOW_REPEAT = 0x01, michael@0: BUFFER_COMPONENT_ALPHA = 0x02 // Dual buffers should be created for drawing with michael@0: // component alpha. michael@0: }; michael@0: /** michael@0: * Return a new surface of |aSize| and |aType|. michael@0: * @param aFlags if ALLOW_REPEAT is set, then the buffer should be configured michael@0: * to allow repeat-mode, otherwise it should be in pad (clamp) mode michael@0: * If the created buffer supports azure content, then the result(s) will michael@0: * be returned in aBlackDT/aWhiteDT, otherwise aBlackSurface/aWhiteSurface michael@0: * will be used. michael@0: */ michael@0: virtual void michael@0: CreateBuffer(ContentType aType, const nsIntRect& aRect, uint32_t aFlags, michael@0: RefPtr* aBlackDT, RefPtr* aWhiteDT) = 0; michael@0: michael@0: /** michael@0: * Get the underlying buffer, if any. This is useful because we can pass michael@0: * in the buffer as the default "reference surface" if there is one. michael@0: * Don't use it for anything else! michael@0: */ michael@0: gfx::DrawTarget* GetDTBuffer() { return mDTBuffer; } michael@0: gfx::DrawTarget* GetDTBufferOnWhite() { return mDTBufferOnWhite; } michael@0: michael@0: /** michael@0: * Complete the drawing operation. The region to draw must have been michael@0: * drawn before this is called. The contents of the buffer are drawn michael@0: * to aTarget. michael@0: */ michael@0: void DrawTo(ThebesLayer* aLayer, michael@0: gfx::DrawTarget* aTarget, michael@0: float aOpacity, michael@0: gfx::CompositionOp aOp, michael@0: gfx::SourceSurface* aMask, michael@0: const gfx::Matrix* aMaskTransform); michael@0: michael@0: protected: michael@0: // new texture client versions michael@0: void SetBufferProvider(TextureClient* aClient) michael@0: { michael@0: // Only this buffer provider can give us a buffer. If we michael@0: // already have one, something has gone wrong. michael@0: MOZ_ASSERT(!aClient || !mDTBuffer); michael@0: michael@0: mBufferProvider = aClient; michael@0: if (!mBufferProvider) { michael@0: mDTBuffer = nullptr; michael@0: } michael@0: } michael@0: michael@0: void SetBufferProviderOnWhite(TextureClient* aClient) michael@0: { michael@0: // Only this buffer provider can give us a buffer. If we michael@0: // already have one, something has gone wrong. michael@0: MOZ_ASSERT(!aClient || !mDTBufferOnWhite); michael@0: michael@0: mBufferProviderOnWhite = aClient; michael@0: if (!mBufferProviderOnWhite) { michael@0: mDTBufferOnWhite = nullptr; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Get a draw target at the specified resolution for updating |aBounds|, michael@0: * which must be contained within a single quadrant. michael@0: * michael@0: * The result should only be held temporarily by the caller (it will be kept michael@0: * alive by this). Once used it should be returned using ReturnDrawTarget. michael@0: * BorrowDrawTargetForQuadrantUpdate may not be called more than once without michael@0: * first calling ReturnDrawTarget. michael@0: * michael@0: * ReturnDrawTarget will restore the transform on the draw target. But it is michael@0: * the callers responsibility to restore the clip. The caller should flush the michael@0: * draw target, if necessary. michael@0: */ michael@0: gfx::DrawTarget* michael@0: BorrowDrawTargetForQuadrantUpdate(const nsIntRect& aBounds, michael@0: ContextSource aSource, michael@0: DrawIterator* aIter); michael@0: michael@0: static bool IsClippingCheap(gfx::DrawTarget* aTarget, const nsIntRegion& aRegion); michael@0: michael@0: protected: michael@0: /** michael@0: * Return the buffer's content type. Requires a valid buffer or michael@0: * buffer provider. michael@0: */ michael@0: gfxContentType BufferContentType(); michael@0: bool BufferSizeOkFor(const nsIntSize& aSize); michael@0: /** michael@0: * If the buffer hasn't been mapped, map it. michael@0: */ michael@0: bool EnsureBuffer(); michael@0: bool EnsureBufferOnWhite(); michael@0: michael@0: // Flush our buffers if they are mapped. michael@0: void FlushBuffers(); michael@0: michael@0: /** michael@0: * True if we have a buffer where we can get it (but not necessarily michael@0: * mapped currently). michael@0: */ michael@0: virtual bool HaveBuffer() const; michael@0: virtual bool HaveBufferOnWhite() const; michael@0: michael@0: /** michael@0: * Any actions that should be performed at the last moment before we begin michael@0: * rendering the next frame. I.e., after we calculate what we will draw, michael@0: * but before we rotate the buffer and possibly create new buffers. michael@0: * aRegionToDraw is the region which is guaranteed to be overwritten when michael@0: * drawing the next frame. michael@0: */ michael@0: virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) {} michael@0: michael@0: /** michael@0: * These members are only set transiently. They're used to map mDTBuffer michael@0: * when we're using surfaces that require explicit map/unmap. Only one michael@0: * may be used at a time. michael@0: */ michael@0: TextureClient* mBufferProvider; michael@0: TextureClient* mBufferProviderOnWhite; michael@0: michael@0: BufferSizePolicy mBufferSizePolicy; michael@0: }; michael@0: michael@0: } michael@0: } michael@0: michael@0: #endif /* ROTATEDBUFFER_H_ */