mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-11 16:32:59 +00:00
265e672179
# ignore-this-changeset --HG-- extra : amend_source : 4d301d3b0b8711c4692392aa76088ba7fd7d1022
420 lines
14 KiB
C++
420 lines
14 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef ROTATEDBUFFER_H_
|
|
#define ROTATEDBUFFER_H_
|
|
|
|
#include "gfxTypes.h"
|
|
#include <stdint.h> // for uint32_t
|
|
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
|
|
#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
|
|
#include "mozilla/gfx/2D.h" // for DrawTarget, etc
|
|
#include "mozilla/gfx/MatrixFwd.h" // for Matrix
|
|
#include "mozilla/layers/TextureClient.h" // for TextureClient
|
|
#include "mozilla/mozalloc.h" // for operator delete
|
|
#include "nsCOMPtr.h" // for already_AddRefed
|
|
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
|
|
#include "nsRegion.h" // for nsIntRegion
|
|
#include "LayersTypes.h"
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
class PaintedLayer;
|
|
class ContentClient;
|
|
|
|
// Mixin class for classes which need logic for loaning out a draw target.
|
|
// See comments on BorrowDrawTargetForQuadrantUpdate.
|
|
class BorrowDrawTarget {
|
|
public:
|
|
void ReturnDrawTarget(gfx::DrawTarget*& aReturned);
|
|
|
|
protected:
|
|
// The draw target loaned by BorrowDrawTargetForQuadrantUpdate. It should not
|
|
// be used, we just keep a reference to ensure it is kept alive and so we can
|
|
// correctly restore state when it is returned.
|
|
RefPtr<gfx::DrawTarget> mLoanedDrawTarget;
|
|
gfx::Matrix mLoanedTransform;
|
|
};
|
|
|
|
/**
|
|
* This is a cairo/Thebes surface, but with a literal twist. Scrolling
|
|
* causes the layer's visible region to move. We want to keep
|
|
* reusing the same surface if the region size hasn't changed, but we don't
|
|
* want to keep moving the contents of the surface around in memory. So
|
|
* we use a trick.
|
|
* Consider just the vertical case, and suppose the buffer is H pixels
|
|
* high and we're scrolling down by N pixels. Instead of copying the
|
|
* buffer contents up by N pixels, we leave the buffer contents in place,
|
|
* and paint content rows H to H+N-1 into rows 0 to N-1 of the buffer.
|
|
* Then we can refresh the screen by painting rows N to H-1 of the buffer
|
|
* at row 0 on the screen, and then painting rows 0 to N-1 of the buffer
|
|
* at row H-N on the screen.
|
|
* mBufferRotation.y would be N in this example.
|
|
*/
|
|
class RotatedBuffer : public BorrowDrawTarget {
|
|
public:
|
|
typedef gfxContentType ContentType;
|
|
|
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RotatedBuffer)
|
|
|
|
RotatedBuffer(const gfx::IntRect& aBufferRect,
|
|
const gfx::IntPoint& aBufferRotation)
|
|
: mCapture(nullptr),
|
|
mBufferRect(aBufferRect),
|
|
mBufferRotation(aBufferRotation),
|
|
mDidSelfCopy(false) {}
|
|
RotatedBuffer() : mCapture(nullptr), mDidSelfCopy(false) {}
|
|
|
|
/**
|
|
* Initializes the rotated buffer to begin capturing all drawing performed
|
|
* on it, to be eventually replayed. Callers must call EndCapture, or
|
|
* FlushCapture before the rotated buffer is destroyed.
|
|
*/
|
|
void BeginCapture();
|
|
|
|
/**
|
|
* Finishes a capture and returns it. The capture must be replayed to the
|
|
* buffer before it is presented or it will contain invalid contents.
|
|
*/
|
|
RefPtr<gfx::DrawTargetCapture> EndCapture();
|
|
|
|
/**
|
|
* Returns whether the RotatedBuffer is currently capturing all drawing
|
|
* performed on it, to be eventually replayed.
|
|
*/
|
|
bool IsCapturing() const { return !!mCapture; }
|
|
|
|
/**
|
|
* Draws the contents of this rotated buffer into the specified draw target.
|
|
* It is the callers repsonsibility to ensure aTarget is flushed after calling
|
|
* this method.
|
|
*/
|
|
void DrawBufferWithRotation(
|
|
gfx::DrawTarget* aTarget, float aOpacity = 1.0,
|
|
gfx::CompositionOp aOperator = gfx::CompositionOp::OP_OVER,
|
|
gfx::SourceSurface* aMask = nullptr,
|
|
const gfx::Matrix* aMaskTransform = nullptr) const;
|
|
|
|
/**
|
|
* Complete the drawing operation. The region to draw must have been
|
|
* drawn before this is called. The contents of the buffer are drawn
|
|
* to aTarget.
|
|
*/
|
|
void DrawTo(PaintedLayer* aLayer, gfx::DrawTarget* aTarget, float aOpacity,
|
|
gfx::CompositionOp aOp, gfx::SourceSurface* aMask,
|
|
const gfx::Matrix* aMaskTransform);
|
|
|
|
/**
|
|
* Update the specified region of this rotated buffer with the contents
|
|
* of a source rotated buffer.
|
|
*/
|
|
void UpdateDestinationFrom(const RotatedBuffer& aSource,
|
|
const gfx::IntRect& aUpdateRect);
|
|
|
|
/**
|
|
* A draw iterator is used to keep track of which quadrant of a rotated
|
|
* buffer and region of that quadrant is being updated.
|
|
*/
|
|
struct DrawIterator {
|
|
friend class RotatedBuffer;
|
|
DrawIterator() : mCount(0) {}
|
|
|
|
nsIntRegion mDrawRegion;
|
|
|
|
private:
|
|
uint32_t mCount;
|
|
};
|
|
|
|
/**
|
|
* Get a draw target at the specified resolution for updating |aBounds|,
|
|
* which must be contained within a single quadrant.
|
|
*
|
|
* The result should only be held temporarily by the caller (it will be kept
|
|
* alive by this). Once used it should be returned using ReturnDrawTarget.
|
|
* BorrowDrawTargetForQuadrantUpdate may not be called more than once without
|
|
* first calling ReturnDrawTarget.
|
|
*
|
|
* ReturnDrawTarget will by default restore the transform on the draw target.
|
|
* But it is the callers responsibility to restore the clip.
|
|
* The caller should flush the draw target, if necessary.
|
|
* If aSetTransform is false, the required transform will be set in
|
|
* aOutTransform.
|
|
*/
|
|
gfx::DrawTarget* BorrowDrawTargetForQuadrantUpdate(
|
|
const gfx::IntRect& aBounds, DrawIterator* aIter);
|
|
|
|
struct Parameters {
|
|
Parameters(const gfx::IntRect& aBufferRect,
|
|
const gfx::IntPoint& aBufferRotation)
|
|
: mBufferRect(aBufferRect),
|
|
mBufferRotation(aBufferRotation),
|
|
mDidSelfCopy(false) {}
|
|
|
|
bool IsRotated() const;
|
|
bool RectWrapsBuffer(const gfx::IntRect& aRect) const;
|
|
|
|
void SetUnrotated();
|
|
|
|
gfx::IntRect mBufferRect;
|
|
gfx::IntPoint mBufferRotation;
|
|
bool mDidSelfCopy;
|
|
};
|
|
|
|
/**
|
|
* Returns the new buffer parameters for rotating to a
|
|
* destination buffer rect.
|
|
*/
|
|
Parameters AdjustedParameters(const gfx::IntRect& aDestBufferRect) const;
|
|
|
|
/**
|
|
* Unrotates the pixels of the rotated buffer for the specified
|
|
* new buffer parameters.
|
|
*/
|
|
bool UnrotateBufferTo(const Parameters& aParameters);
|
|
|
|
void SetParameters(const Parameters& aParameters);
|
|
|
|
/**
|
|
* |BufferRect()| is the rect of device pixels that this
|
|
* RotatedBuffer covers. That is what DrawBufferWithRotation()
|
|
* will paint when it's called.
|
|
*/
|
|
const gfx::IntRect& BufferRect() const { return mBufferRect; }
|
|
const gfx::IntPoint& BufferRotation() const { return mBufferRotation; }
|
|
|
|
/**
|
|
* Overrides the current buffer rect with the specified rect.
|
|
* Do not do this unless you know what you're doing.
|
|
*/
|
|
void SetBufferRect(const gfx::IntRect& aBufferRect) {
|
|
mBufferRect = aBufferRect;
|
|
}
|
|
|
|
/**
|
|
* Overrides the current buffer rotation with the specified point.
|
|
* Do not do this unless you know what you're doing.
|
|
*/
|
|
void SetBufferRotation(const gfx::IntPoint& aBufferRotation) {
|
|
mBufferRotation = aBufferRotation;
|
|
}
|
|
|
|
/**
|
|
* Returns whether this buffer did a self copy when adjusting to
|
|
* a new buffer rect. This is only used externally for syncing
|
|
* rotated buffers.
|
|
*/
|
|
bool DidSelfCopy() const { return mDidSelfCopy; }
|
|
|
|
/**
|
|
* Clears the self copy flag.
|
|
*/
|
|
void ClearDidSelfCopy() { mDidSelfCopy = false; }
|
|
|
|
/**
|
|
* Gets the content type for this buffer.
|
|
*/
|
|
ContentType GetContentType() const;
|
|
|
|
virtual bool IsLocked() = 0;
|
|
virtual bool Lock(OpenMode aMode) = 0;
|
|
virtual void Unlock() = 0;
|
|
|
|
virtual bool HaveBuffer() const = 0;
|
|
virtual bool HaveBufferOnWhite() const = 0;
|
|
|
|
virtual gfx::SurfaceFormat GetFormat() const = 0;
|
|
|
|
virtual already_AddRefed<gfx::SourceSurface> GetBufferSource() const {
|
|
return GetBufferTarget()->Snapshot();
|
|
}
|
|
virtual gfx::DrawTarget* GetBufferTarget() const = 0;
|
|
|
|
virtual TextureClient* GetClient() const { return nullptr; }
|
|
virtual TextureClient* GetClientOnWhite() const { return nullptr; }
|
|
|
|
protected:
|
|
virtual ~RotatedBuffer() { MOZ_ASSERT(!mCapture); }
|
|
|
|
enum XSide { LEFT, RIGHT };
|
|
enum YSide { TOP, BOTTOM };
|
|
gfx::IntRect GetQuadrantRectangle(XSide aXSide, YSide aYSide) const;
|
|
|
|
gfx::Rect GetSourceRectangle(XSide aXSide, YSide aYSide) const;
|
|
|
|
gfx::DrawTarget* GetDrawTarget() const {
|
|
if (mCapture) {
|
|
return mCapture;
|
|
}
|
|
return GetBufferTarget();
|
|
}
|
|
|
|
/*
|
|
* If aMask is non-null, then it is used as an alpha mask for rendering this
|
|
* buffer. aMaskTransform must be non-null if aMask is non-null, and is used
|
|
* to adjust the coordinate space of the mask.
|
|
*/
|
|
void DrawBufferQuadrant(gfx::DrawTarget* aTarget, XSide aXSide, YSide aYSide,
|
|
float aOpacity, gfx::CompositionOp aOperator,
|
|
gfx::SourceSurface* aMask,
|
|
const gfx::Matrix* aMaskTransform) const;
|
|
|
|
RefPtr<gfx::DrawTargetCapture> mCapture;
|
|
|
|
/** The area of the PaintedLayer that is covered by the buffer as a whole */
|
|
gfx::IntRect mBufferRect;
|
|
/**
|
|
* The x and y rotation of the buffer. Conceptually the buffer
|
|
* has its origin translated to mBufferRect.TopLeft() - mBufferRotation,
|
|
* is tiled to fill the plane, and the result is clipped to mBufferRect.
|
|
* So the pixel at mBufferRotation within the buffer is what gets painted at
|
|
* mBufferRect.TopLeft().
|
|
* This is "rotation" in the sense of rotating items in a linear buffer,
|
|
* where items falling off the end of the buffer are returned to the
|
|
* buffer at the other end, not 2D rotation!
|
|
*/
|
|
gfx::IntPoint mBufferRotation;
|
|
/**
|
|
* When this is true it means that all pixels have moved inside the buffer.
|
|
* It's not possible to sync with another buffer without a full copy.
|
|
*/
|
|
bool mDidSelfCopy;
|
|
};
|
|
|
|
/**
|
|
* RemoteRotatedBuffer is a rotated buffer that is backed by texture
|
|
* clients. Before you use this class you must successfully lock it with
|
|
* an appropriate open mode, and then also unlock it when you're finished.
|
|
* RemoteRotatedBuffer is used by ContentClientSingleBuffered and
|
|
* ContentClientDoubleBuffered for the OMTC code path.
|
|
*/
|
|
class RemoteRotatedBuffer : public RotatedBuffer {
|
|
public:
|
|
RemoteRotatedBuffer(TextureClient* aClient, TextureClient* aClientOnWhite,
|
|
const gfx::IntRect& aBufferRect,
|
|
const gfx::IntPoint& aBufferRotation)
|
|
: RotatedBuffer(aBufferRect, aBufferRotation),
|
|
mClient(aClient),
|
|
mClientOnWhite(aClientOnWhite) {}
|
|
|
|
virtual bool IsLocked() override;
|
|
virtual bool Lock(OpenMode aMode) override;
|
|
virtual void Unlock() override;
|
|
|
|
virtual bool HaveBuffer() const override { return !!mClient; }
|
|
virtual bool HaveBufferOnWhite() const override { return !!mClientOnWhite; }
|
|
|
|
virtual gfx::SurfaceFormat GetFormat() const override;
|
|
|
|
virtual gfx::DrawTarget* GetBufferTarget() const override;
|
|
|
|
virtual TextureClient* GetClient() const override { return mClient; }
|
|
virtual TextureClient* GetClientOnWhite() const override {
|
|
return mClientOnWhite;
|
|
}
|
|
|
|
void SyncWithObject(SyncObjectClient* aSyncObject);
|
|
void Clear();
|
|
|
|
private:
|
|
RemoteRotatedBuffer(TextureClient* aClient, TextureClient* aClientOnWhite,
|
|
gfx::DrawTarget* aTarget, gfx::DrawTarget* aTargetOnWhite,
|
|
gfx::DrawTarget* aTargetDual,
|
|
const gfx::IntRect& aBufferRect,
|
|
const gfx::IntPoint& aBufferRotation)
|
|
: RotatedBuffer(aBufferRect, aBufferRotation),
|
|
mClient(aClient),
|
|
mClientOnWhite(aClientOnWhite),
|
|
mTarget(aTarget),
|
|
mTargetOnWhite(aTargetOnWhite),
|
|
mTargetDual(aTargetDual) {}
|
|
|
|
RefPtr<TextureClient> mClient;
|
|
RefPtr<TextureClient> mClientOnWhite;
|
|
|
|
RefPtr<gfx::DrawTarget> mTarget;
|
|
RefPtr<gfx::DrawTarget> mTargetOnWhite;
|
|
RefPtr<gfx::DrawTarget> mTargetDual;
|
|
};
|
|
|
|
/**
|
|
* DrawTargetRotatedBuffer is a rotated buffer that is backed by draw targets,
|
|
* and is used by ContentClientBasic for the on-mtc code path.
|
|
*/
|
|
class DrawTargetRotatedBuffer : public RotatedBuffer {
|
|
public:
|
|
DrawTargetRotatedBuffer(gfx::DrawTarget* aTarget,
|
|
gfx::DrawTarget* aTargetOnWhite,
|
|
const gfx::IntRect& aBufferRect,
|
|
const gfx::IntPoint& aBufferRotation)
|
|
: RotatedBuffer(aBufferRect, aBufferRotation),
|
|
mTarget(aTarget),
|
|
mTargetOnWhite(aTargetOnWhite) {
|
|
if (mTargetOnWhite) {
|
|
mTargetDual = gfx::Factory::CreateDualDrawTarget(mTarget, mTargetOnWhite);
|
|
} else {
|
|
mTargetDual = mTarget;
|
|
}
|
|
}
|
|
|
|
virtual bool IsLocked() override { return false; }
|
|
virtual bool Lock(OpenMode aMode) override { return true; }
|
|
virtual void Unlock() override {}
|
|
|
|
virtual bool HaveBuffer() const override { return !!mTargetDual; }
|
|
virtual bool HaveBufferOnWhite() const override { return !!mTargetOnWhite; }
|
|
|
|
virtual gfx::SurfaceFormat GetFormat() const override;
|
|
|
|
virtual gfx::DrawTarget* GetBufferTarget() const override;
|
|
|
|
private:
|
|
RefPtr<gfx::DrawTarget> mTarget;
|
|
RefPtr<gfx::DrawTarget> mTargetOnWhite;
|
|
RefPtr<gfx::DrawTarget> mTargetDual;
|
|
};
|
|
|
|
/**
|
|
* SourceRotatedBuffer is a rotated buffer that is backed by source surfaces,
|
|
* and may only be used to draw into other buffers or be read directly.
|
|
*/
|
|
class SourceRotatedBuffer : public RotatedBuffer {
|
|
public:
|
|
SourceRotatedBuffer(gfx::SourceSurface* aSource,
|
|
gfx::SourceSurface* aSourceOnWhite,
|
|
const gfx::IntRect& aBufferRect,
|
|
const gfx::IntPoint& aBufferRotation)
|
|
: RotatedBuffer(aBufferRect, aBufferRotation),
|
|
mSource(aSource),
|
|
mSourceOnWhite(aSourceOnWhite) {
|
|
mSourceDual =
|
|
gfx::Factory::CreateDualSourceSurface(mSource, mSourceOnWhite);
|
|
}
|
|
|
|
virtual bool IsLocked() override { return false; }
|
|
virtual bool Lock(OpenMode aMode) override { return false; }
|
|
virtual void Unlock() override {}
|
|
|
|
virtual already_AddRefed<gfx::SourceSurface> GetBufferSource() const override;
|
|
|
|
virtual gfx::SurfaceFormat GetFormat() const override;
|
|
|
|
virtual bool HaveBuffer() const override { return !!mSourceDual; }
|
|
virtual bool HaveBufferOnWhite() const override { return !!mSourceOnWhite; }
|
|
|
|
virtual gfx::DrawTarget* GetBufferTarget() const override { return nullptr; }
|
|
|
|
private:
|
|
RefPtr<gfx::SourceSurface> mSource;
|
|
RefPtr<gfx::SourceSurface> mSourceOnWhite;
|
|
RefPtr<gfx::SourceSurface> mSourceDual;
|
|
};
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|
|
|
|
#endif /* ROTATEDBUFFER_H_ */
|