зеркало из https://github.com/mozilla/gecko-dev.git
Bug 675837 - Add 3D transforms support to BasicLayers. r=roc
This commit is contained in:
Родитель
6c63c596b7
Коммит
7e10034edd
|
@ -63,6 +63,7 @@
|
|||
#endif
|
||||
|
||||
#include "GLContext.h"
|
||||
#include "pixman.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
@ -237,8 +238,17 @@ public:
|
|||
// We push groups for container layers if we need to, which always
|
||||
// are aligned in device space, so it doesn't really matter how we snap
|
||||
// containers.
|
||||
gfxMatrix residual;
|
||||
gfx3DMatrix idealTransform = GetLocalTransform()*aTransformToSurface;
|
||||
mEffectiveTransform = SnapTransform(idealTransform, gfxRect(0, 0, 0, 0), nsnull);
|
||||
|
||||
if (!idealTransform.CanDraw2D()) {
|
||||
mEffectiveTransform = idealTransform;
|
||||
ComputeEffectiveTransformsForChildren(gfx3DMatrix());
|
||||
mUseIntermediateSurface = PR_TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
mEffectiveTransform = SnapTransform(idealTransform, gfxRect(0, 0, 0, 0), &residual);
|
||||
// We always pass the ideal matrix down to our children, so there is no
|
||||
// need to apply any compensation using the residual from SnapTransform.
|
||||
ComputeEffectiveTransformsForChildren(idealTransform);
|
||||
|
@ -288,7 +298,7 @@ PRBool
|
|||
BasicContainerLayer::ChildrenPartitionVisibleRegion(const nsIntRect& aInRect)
|
||||
{
|
||||
gfxMatrix transform;
|
||||
if (!GetEffectiveTransform().Is2D(&transform) ||
|
||||
if (!GetEffectiveTransform().CanDraw2D(&transform) ||
|
||||
transform.HasNonIntegerTranslation())
|
||||
return PR_FALSE;
|
||||
|
||||
|
@ -301,7 +311,7 @@ BasicContainerLayer::ChildrenPartitionVisibleRegion(const nsIntRect& aInRect)
|
|||
continue;
|
||||
|
||||
gfxMatrix childTransform;
|
||||
if (!l->GetEffectiveTransform().Is2D(&childTransform) ||
|
||||
if (!l->GetEffectiveTransform().CanDraw2D(&childTransform) ||
|
||||
childTransform.HasNonIntegerTranslation() ||
|
||||
l->GetEffectiveOpacity() != 1.0)
|
||||
return PR_FALSE;
|
||||
|
@ -704,7 +714,7 @@ BasicThebesLayer::PaintThebes(gfxContext* aContext,
|
|||
{
|
||||
PRUint32 flags = 0;
|
||||
gfxMatrix transform;
|
||||
if (!GetEffectiveTransform().Is2D(&transform) ||
|
||||
if (!GetEffectiveTransform().CanDraw2D(&transform) ||
|
||||
transform.HasNonIntegerTranslation() ||
|
||||
MustRetainContent() /*<=> has shadow layer*/) {
|
||||
flags |= ThebesLayerBuffer::PAINT_WILL_RESAMPLE;
|
||||
|
@ -1355,6 +1365,18 @@ BasicLayerManager::PopGroupToSourceWithCachedSurface(gfxContext *aTarget, gfxCon
|
|||
}
|
||||
}
|
||||
|
||||
already_AddRefed<gfxASurface>
|
||||
BasicLayerManager::PopGroupToSurface(gfxContext *aTarget, gfxContext *aPushed)
|
||||
{
|
||||
if (!aTarget)
|
||||
return nsnull;
|
||||
nsRefPtr<gfxASurface> current = aPushed->CurrentSurface();
|
||||
NS_ASSERTION(!mCachedSurface.IsSurface(current), "Should never be popping cached surface here!");
|
||||
nsRefPtr<gfxPattern> pat = aTarget->PopGroup();
|
||||
current = pat->GetSurface();
|
||||
return current.forget();
|
||||
}
|
||||
|
||||
void
|
||||
BasicLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
|
||||
{
|
||||
|
@ -1420,7 +1442,7 @@ MarkLayersHidden(Layer* aLayer, const nsIntRect& aClipRect,
|
|||
// global coordinate system.
|
||||
if (aLayer->GetParent()) {
|
||||
gfxMatrix tr;
|
||||
if (aLayer->GetParent()->GetEffectiveTransform().Is2D(&tr)) {
|
||||
if (aLayer->GetParent()->GetEffectiveTransform().CanDraw2D(&tr)) {
|
||||
// Clip rect is applied after aLayer's transform, i.e., in the coordinate
|
||||
// system of aLayer's parent.
|
||||
TransformIntRect(cr, tr, ToInsideIntRect);
|
||||
|
@ -1438,7 +1460,7 @@ MarkLayersHidden(Layer* aLayer, const nsIntRect& aClipRect,
|
|||
|
||||
if (!aLayer->AsContainerLayer()) {
|
||||
gfxMatrix transform;
|
||||
if (!aLayer->GetEffectiveTransform().Is2D(&transform)) {
|
||||
if (!aLayer->GetEffectiveTransform().CanDraw2D(&transform)) {
|
||||
data->SetHidden(PR_FALSE);
|
||||
return;
|
||||
}
|
||||
|
@ -1499,7 +1521,7 @@ ApplyDoubleBuffering(Layer* aLayer, const nsIntRect& aVisibleRect)
|
|||
// global coordinate system.
|
||||
if (aLayer->GetParent()) {
|
||||
gfxMatrix tr;
|
||||
if (aLayer->GetParent()->GetEffectiveTransform().Is2D(&tr)) {
|
||||
if (aLayer->GetParent()->GetEffectiveTransform().CanDraw2D(&tr)) {
|
||||
NS_ASSERTION(!tr.HasNonIntegerTranslation(),
|
||||
"Parent can only have an integer translation");
|
||||
cr += nsIntPoint(PRInt32(tr.x0), PRInt32(tr.y0));
|
||||
|
@ -1640,6 +1662,143 @@ BasicLayerManager::SetRoot(Layer* aLayer)
|
|||
mRoot = aLayer;
|
||||
}
|
||||
|
||||
static pixman_transform
|
||||
Matrix3DToPixman(const gfx3DMatrix& aMatrix)
|
||||
{
|
||||
pixman_f_transform transform;
|
||||
|
||||
transform.m[0][0] = aMatrix._11;
|
||||
transform.m[0][1] = aMatrix._21;
|
||||
transform.m[0][2] = aMatrix._41;
|
||||
transform.m[1][0] = aMatrix._12;
|
||||
transform.m[1][1] = aMatrix._22;
|
||||
transform.m[1][2] = aMatrix._42;
|
||||
transform.m[2][0] = aMatrix._14;
|
||||
transform.m[2][1] = aMatrix._24;
|
||||
transform.m[2][2] = aMatrix._44;
|
||||
|
||||
pixman_transform result;
|
||||
pixman_transform_from_pixman_f_transform(&result, &transform);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
PixmanTransform(const gfxImageSurface *aDest,
|
||||
const gfxImageSurface *aSrc,
|
||||
const gfx3DMatrix& aTransform,
|
||||
gfxPoint aDestOffset)
|
||||
{
|
||||
gfxIntSize destSize = aDest->GetSize();
|
||||
pixman_image_t* dest = pixman_image_create_bits(aDest->Format() == gfxASurface::ImageFormatARGB32 ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
|
||||
destSize.width,
|
||||
destSize.height,
|
||||
(uint32_t*)aDest->Data(),
|
||||
aDest->Stride());
|
||||
|
||||
gfxIntSize srcSize = aSrc->GetSize();
|
||||
pixman_image_t* src = pixman_image_create_bits(aSrc->Format() == gfxASurface::ImageFormatARGB32 ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
|
||||
srcSize.width,
|
||||
srcSize.height,
|
||||
(uint32_t*)aSrc->Data(),
|
||||
aSrc->Stride());
|
||||
|
||||
NS_ABORT_IF_FALSE(src && dest, "Failed to create pixman images?");
|
||||
|
||||
pixman_transform pixTransform = Matrix3DToPixman(aTransform);
|
||||
pixman_transform pixTransformInverted;
|
||||
|
||||
// If the transform is singular then nothing would be drawn anyway, return here
|
||||
if (!pixman_transform_invert(&pixTransformInverted, &pixTransform)) {
|
||||
return;
|
||||
}
|
||||
pixman_image_set_transform(src, &pixTransformInverted);
|
||||
|
||||
pixman_image_composite32(PIXMAN_OP_SRC,
|
||||
src,
|
||||
nsnull,
|
||||
dest,
|
||||
aDestOffset.x,
|
||||
aDestOffset.y,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
destSize.width,
|
||||
destSize.height);
|
||||
|
||||
pixman_image_unref(dest);
|
||||
pixman_image_unref(src);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a surface using a gfx3DMatrix and blit to the destination if
|
||||
* it is efficient to do so.
|
||||
*
|
||||
* @param aSource Source surface.
|
||||
* @param aDest Desintation context.
|
||||
* @param aBounds Area represented by aSource.
|
||||
* @param aTransform Transformation matrix.
|
||||
* @param aDrawOffset Location to draw returned surface on aDest.
|
||||
* @param aDontBlit Never draw to aDest if this is true.
|
||||
* @return Transformed surface, or nsnull if it has been drawn to aDest.
|
||||
*/
|
||||
static already_AddRefed<gfxASurface>
|
||||
Transform3D(gfxASurface* aSource, gfxContext* aDest,
|
||||
const gfxRect& aBounds, const gfx3DMatrix& aTransform,
|
||||
gfxPoint& aDrawOffset, PRBool aDontBlit)
|
||||
{
|
||||
nsRefPtr<gfxImageSurface> sourceImage = aSource->GetAsImageSurface();
|
||||
if (!sourceImage) {
|
||||
sourceImage = new gfxImageSurface(gfxIntSize(aBounds.width, aBounds.height), gfxASurface::FormatFromContent(aSource->GetContentType()));
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(sourceImage);
|
||||
|
||||
aSource->SetDeviceOffset(gfxPoint(0, 0));
|
||||
ctx->SetSource(aSource);
|
||||
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
ctx->Paint();
|
||||
}
|
||||
|
||||
// Find the transformed rectangle of our layer.
|
||||
gfxRect offsetRect = aTransform.TransformBounds(aBounds);
|
||||
|
||||
// Intersect the transformed layer with the destination rectangle.
|
||||
// This is in device space since we have an identity transform set on aTarget.
|
||||
gfxRect destRect = aDest->GetClipExtents();
|
||||
destRect.IntersectRect(destRect, offsetRect);
|
||||
|
||||
// Create a surface the size of the transformed object.
|
||||
nsRefPtr<gfxASurface> dest = aDest->CurrentSurface();
|
||||
nsRefPtr<gfxImageSurface> destImage = dest->GetAsImageSurface();
|
||||
destImage = nsnull;
|
||||
gfxPoint offset;
|
||||
PRBool blitComplete;
|
||||
if (!destImage || aDontBlit || !aDest->ClipContainsRect(destRect)) {
|
||||
destImage = new gfxImageSurface(gfxIntSize(destRect.width, destRect.height),
|
||||
gfxASurface::ImageFormatARGB32);
|
||||
offset = destRect.TopLeft();
|
||||
blitComplete = PR_FALSE;
|
||||
} else {
|
||||
offset = -dest->GetDeviceOffset();
|
||||
blitComplete = PR_TRUE;
|
||||
}
|
||||
|
||||
// Include a translation to the correct origin.
|
||||
gfx3DMatrix translation = gfx3DMatrix::Translation(aBounds.x, aBounds.y, 0);
|
||||
|
||||
// Transform the content and offset it such that the content begins at the origin.
|
||||
PixmanTransform(destImage, sourceImage, translation * aTransform, offset);
|
||||
|
||||
if (blitComplete) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
// If we haven't actually drawn to aDest then return our temporary image so that
|
||||
// the caller can do this.
|
||||
aDrawOffset = destRect.TopLeft();
|
||||
return destImage.forget();
|
||||
}
|
||||
|
||||
void
|
||||
BasicLayerManager::PaintLayer(gfxContext* aTarget,
|
||||
Layer* aLayer,
|
||||
|
@ -1676,12 +1835,16 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget,
|
|||
}
|
||||
|
||||
gfxMatrix transform;
|
||||
// XXX we need to add some kind of 3D transform support, possibly
|
||||
// using pixman?
|
||||
NS_ASSERTION(effectiveTransform.Is2D(),
|
||||
"Only 2D transforms supported currently");
|
||||
effectiveTransform.Is2D(&transform);
|
||||
aTarget->SetMatrix(transform);
|
||||
// Will return an identity matrix for 3d transforms, and is handled separately below.
|
||||
PRBool is2D = effectiveTransform.CanDraw2D(&transform);
|
||||
NS_ABORT_IF_FALSE(is2D || needsGroup || !aLayer->GetFirstChild(), "Must PushGroup for 3d transforms!");
|
||||
if (is2D) {
|
||||
aTarget->SetMatrix(transform);
|
||||
} else {
|
||||
aTarget->SetMatrix(gfxMatrix());
|
||||
// Save so we can restore clipping after PushGroupForLayer changes it.
|
||||
aTarget->Save();
|
||||
}
|
||||
|
||||
const nsIntRegion& visibleRegion = aLayer->GetEffectiveVisibleRegion();
|
||||
// If needsGroup is true, we'll clip to the visible region after we've popped the group
|
||||
|
@ -1741,7 +1904,27 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget,
|
|||
}
|
||||
|
||||
if (needsGroup) {
|
||||
PopGroupToSourceWithCachedSurface(aTarget, groupTarget);
|
||||
PRBool blitComplete = PR_FALSE;
|
||||
if (is2D) {
|
||||
PopGroupToSourceWithCachedSurface(aTarget, groupTarget);
|
||||
} else {
|
||||
nsRefPtr<gfxASurface> sourceSurface = PopGroupToSurface(aTarget, groupTarget);
|
||||
aTarget->Restore();
|
||||
NS_ABORT_IF_FALSE(sourceSurface, "PopGroup should always return a surface pattern");
|
||||
gfxRect bounds = visibleRegion.GetBounds();
|
||||
|
||||
gfxPoint offset;
|
||||
PRBool dontBlit = needsClipToVisibleRegion || mTransactionIncomplete ||
|
||||
aLayer->GetEffectiveOpacity() != 1.0f;
|
||||
nsRefPtr<gfxASurface> result =
|
||||
Transform3D(sourceSurface, aTarget, bounds,
|
||||
effectiveTransform, offset, dontBlit);
|
||||
|
||||
blitComplete = !result;
|
||||
if (result) {
|
||||
aTarget->SetSource(result, offset);
|
||||
}
|
||||
}
|
||||
// If we're doing our own double-buffering, we need to avoid drawing
|
||||
// the results of an incomplete transaction to the destination surface ---
|
||||
// that could cause flicker. Double-buffering is implemented using a
|
||||
|
@ -1751,7 +1934,7 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget,
|
|||
// intersect any other leaf layers, so if the transaction is not yet marked
|
||||
// incomplete, the contents of this container layer are the final contents
|
||||
// for the window.
|
||||
if (!mTransactionIncomplete) {
|
||||
if (!mTransactionIncomplete && !blitComplete) {
|
||||
if (needsClipToVisibleRegion) {
|
||||
gfxUtils::ClipToRegion(aTarget, aLayer->GetEffectiveVisibleRegion());
|
||||
}
|
||||
|
@ -2677,8 +2860,17 @@ public:
|
|||
// We push groups for container layers if we need to, which always
|
||||
// are aligned in device space, so it doesn't really matter how we snap
|
||||
// containers.
|
||||
gfxMatrix residual;
|
||||
gfx3DMatrix idealTransform = GetLocalTransform()*aTransformToSurface;
|
||||
mEffectiveTransform = SnapTransform(idealTransform, gfxRect(0, 0, 0, 0), nsnull);
|
||||
|
||||
if (!idealTransform.CanDraw2D()) {
|
||||
mEffectiveTransform = idealTransform;
|
||||
ComputeEffectiveTransformsForChildren(gfx3DMatrix());
|
||||
mUseIntermediateSurface = PR_TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
mEffectiveTransform = SnapTransform(idealTransform, gfxRect(0, 0, 0, 0), &residual);
|
||||
// We always pass the ideal matrix down to our children, so there is no
|
||||
// need to apply any compensation using the residual from SnapTransform.
|
||||
ComputeEffectiveTransformsForChildren(idealTransform);
|
||||
|
|
|
@ -171,6 +171,7 @@ public:
|
|||
already_AddRefed<gfxContext> PushGroupWithCachedSurface(gfxContext *aTarget,
|
||||
gfxASurface::gfxContentType aContent);
|
||||
void PopGroupToSourceWithCachedSurface(gfxContext *aTarget, gfxContext *aPushed);
|
||||
already_AddRefed<gfxASurface> PopGroupToSurface(gfxContext *aTarget, gfxContext *aPushed);
|
||||
|
||||
virtual PRBool IsCompositingCheap() { return PR_FALSE; }
|
||||
virtual bool HasShadowManagerInternal() const { return false; }
|
||||
|
|
|
@ -347,6 +347,24 @@ gfx3DMatrix::Is2D(gfxMatrix* aMatrix) const
|
|||
return PR_TRUE;
|
||||
}
|
||||
|
||||
PRBool
|
||||
gfx3DMatrix::CanDraw2D(gfxMatrix* aMatrix) const
|
||||
{
|
||||
if (_14 != 0.0f || _24 != 0.0f ||
|
||||
_34 != 0.0f || _44 != 1.0f) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
if (aMatrix) {
|
||||
aMatrix->xx = _11;
|
||||
aMatrix->yx = _12;
|
||||
aMatrix->xy = _21;
|
||||
aMatrix->yy = _22;
|
||||
aMatrix->x0 = _41;
|
||||
aMatrix->y0 = _42;
|
||||
}
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
gfxPoint gfx3DMatrix::ProjectPoint(const gfxPoint& aPoint) const
|
||||
{
|
||||
// Define a ray of the form P + Ut where t is a real number
|
||||
|
|
|
@ -96,6 +96,17 @@ public:
|
|||
*/
|
||||
PRBool Is2D(gfxMatrix* aMatrix = nsnull) const;
|
||||
|
||||
/**
|
||||
* Returns true if the matrix can be reduced to a 2D affine transformation
|
||||
* (i.e. as obtained by From2D). If it is, optionally returns the 2D
|
||||
* matrix in aMatrix. This should only be used on matrices required for
|
||||
* rendering, not for intermediate calculations.
|
||||
*
|
||||
* Since drawing is to a 2d plane, any 3d transform without perspective
|
||||
* can be reduced by dropping the z row and column.
|
||||
*/
|
||||
PRBool CanDraw2D(gfxMatrix* aMatrix = nsnull) const;
|
||||
|
||||
/**
|
||||
* Returns true if the matrix is the identity matrix. The most important
|
||||
* property we require is that gfx3DMatrix().IsIdentity() returns true.
|
||||
|
|
Загрузка…
Ссылка в новой задаче