bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSampleTime,
                                                            const FrameMetrics& aFrame,
                                                            const gfx3DMatrix& aCurrentTransform,
                                                            gfx3DMatrix* aNewTransform) {
  // The eventual return value of this function. The compositor needs to know
  // whether or not to advance by a frame as soon as it can. For example, if a
  // fling is happening, it has to keep compositing so that the animation is
  // smooth. If an animation frame is requested, it is the compositor's
  // responsibility to schedule a composite.
  bool requestAnimationFrame = false;

  // Scales on the root layer, on what's currently painted.
  float rootScaleX = aCurrentTransform.GetXScale(),
        rootScaleY = aCurrentTransform.GetYScale();

  nsIntPoint metricsScrollOffset(0, 0);
  nsIntPoint scrollOffset;
  float localScaleX, localScaleY;

  {
    MonitorAutoLock mon(mMonitor);

    // If a fling is currently happening, apply it now. We can pull the updated
    // metrics afterwards.
    requestAnimationFrame = requestAnimationFrame || DoFling(aSampleTime - mLastSampleTime);

    // Current local transform; this is not what's painted but rather what PZC has
    // transformed due to touches like panning or pinching. Eventually, the root
    // layer transform will become this during runtime, but we must wait for Gecko
    // to repaint.
    localScaleX = mFrameMetrics.mResolution.width;
    localScaleY = mFrameMetrics.mResolution.height;

    if (aFrame.IsScrollable()) {
      metricsScrollOffset = aFrame.mViewportScrollOffset;
    }

    scrollOffset = mFrameMetrics.mViewportScrollOffset;
  }

  nsIntPoint scrollCompensation(
    (scrollOffset.x / rootScaleX - metricsScrollOffset.x) * localScaleX,
    (scrollOffset.y / rootScaleY - metricsScrollOffset.y) * localScaleY);

  ViewTransform treeTransform(-scrollCompensation, localScaleX, localScaleY);
  *aNewTransform = gfx3DMatrix(treeTransform) * aCurrentTransform;

  mLastSampleTime = aSampleTime;

  return requestAnimationFrame;
}
Example #2
0
/**
 * Recover the z component from a 2d transformed point by finding the intersection
 * of a line through the point in the z direction and the transformed plane.
 *
 * We want to solve:
 *
 * point = normal . (p0 - l0) / normal . l
 */
static gfxFloat RecoverZDepth(const gfx3DMatrix& aTransform, const gfxPoint& aPoint)
{
    const gfxPoint3D l(0, 0, 1);
    gfxPoint3D l0 = gfxPoint3D(aPoint.x, aPoint.y, 0);
    gfxPoint3D p0 = aTransform.Transform3D(gfxPoint3D(0, 0, 0));
    gfxPoint3D normal = aTransform.GetNormalVector();

    gfxFloat n = normal.DotProduct(p0 - l0); 
    gfxFloat d = normal.DotProduct(l);

    if (!d) {
        return 0;
    }

    return n/d;
}
static LayoutDeviceRect
ApplyScreenToLayoutTransform(const gfx3DMatrix& aTransform, const ScreenRect& aScreenRect)
{
  gfxRect input(aScreenRect.x, aScreenRect.y, aScreenRect.width, aScreenRect.height);
  gfxRect output = aTransform.TransformBounds(input);
  return LayoutDeviceRect(output.x, output.y, output.width, output.height);
}
/* Helper function to process a matrix entry. */
static void
ProcessMatrix(gfx3DMatrix& aMatrix,
              const nsCSSValue::Array* aData,
              nsStyleContext* aContext,
              nsPresContext* aPresContext,
              bool& aCanStoreInRuleTree,
              nsRect& aBounds)
{
  NS_PRECONDITION(aData->Count() == 7, "Invalid array!");

  gfxMatrix result;

  /* Take the first four elements out of the array as floats and store
   * them.
   */
  result._11 = aData->Item(1).GetFloatValue();
  result._12 = aData->Item(2).GetFloatValue();
  result._21 = aData->Item(3).GetFloatValue();
  result._22 = aData->Item(4).GetFloatValue();

  /* The last two elements have their length parts stored in aDelta
   * and their percent parts stored in aX[0] and aY[1].
   */
  result._31 = ProcessTranslatePart(aData->Item(5),
                                   aContext, aPresContext, aCanStoreInRuleTree,
                                   aBounds.Width());
  result._32 = ProcessTranslatePart(aData->Item(6),
                                   aContext, aPresContext, aCanStoreInRuleTree,
                                   aBounds.Height());

  aMatrix.PreMultiply(result);
}
static void
ProcessRotateY(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
{
  NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
  double theta = aData->Item(1).GetAngleValueInRadians();
  aMatrix.RotateY(theta);
}
static void
ProcessTranslate3D(gfx3DMatrix& aMatrix,
                   const nsCSSValue::Array* aData,
                   nsStyleContext* aContext,
                   nsPresContext* aPresContext,
                   bool& aCanStoreInRuleTree,
                   nsRect& aBounds)
{
  NS_PRECONDITION(aData->Count() == 4, "Invalid array!");

  Point3D temp;

  temp.x = ProcessTranslatePart(aData->Item(1),
                                aContext, aPresContext, aCanStoreInRuleTree,
                                aBounds.Width());

  temp.y = ProcessTranslatePart(aData->Item(2),
                                aContext, aPresContext, aCanStoreInRuleTree,
                                aBounds.Height());

  temp.z = ProcessTranslatePart(aData->Item(3),
                                aContext, aPresContext, aCanStoreInRuleTree,
                                0);

  aMatrix.Translate(temp);
}
Example #7
0
gfx3DMatrix
Layer::SnapTransformTranslation(const gfx3DMatrix& aTransform,
                                gfxMatrix* aResidualTransform)
{
  if (aResidualTransform) {
    *aResidualTransform = gfxMatrix();
  }

  gfxMatrix matrix2D;
  gfx3DMatrix result;
  if (!(mContentFlags & CONTENT_DISABLE_TRANSFORM_SNAPPING) &&
      mManager->IsSnappingEffectiveTransforms() &&
      aTransform.Is2D(&matrix2D) &&
      !matrix2D.HasNonTranslation() &&
      matrix2D.HasNonIntegerTranslation()) {
    gfxPoint snappedTranslation(matrix2D.GetTranslation());
    snappedTranslation.Round();
    gfxMatrix snappedMatrix = gfxMatrix().Translate(snappedTranslation);
    result = gfx3DMatrix::From2D(snappedMatrix);
    if (aResidualTransform) {
      // set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
      // (I.e., appying snappedMatrix after aResidualTransform gives the
      // ideal transform.)
      *aResidualTransform =
        gfxMatrix().Translate(matrix2D.GetTranslation() - snappedTranslation);
    }
  } else {
    result = aTransform;
  }
  return result;
}
Example #8
0
gfx3DMatrix
gfx3DMatrix::operator*(const gfx3DMatrix &aMatrix) const
{
  if (Is2D() && aMatrix.Is2D()) {
    return Multiply2D(aMatrix);
  }

  gfx3DMatrix matrix;

  matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21 + _13 * aMatrix._31 + _14 * aMatrix._41;
  matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21 + _23 * aMatrix._31 + _24 * aMatrix._41;
  matrix._31 = _31 * aMatrix._11 + _32 * aMatrix._21 + _33 * aMatrix._31 + _34 * aMatrix._41;
  matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + _43 * aMatrix._31 + _44 * aMatrix._41;
  matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22 + _13 * aMatrix._32 + _14 * aMatrix._42;
  matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22 + _23 * aMatrix._32 + _24 * aMatrix._42;
  matrix._32 = _31 * aMatrix._12 + _32 * aMatrix._22 + _33 * aMatrix._32 + _34 * aMatrix._42;
  matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + _43 * aMatrix._32 + _44 * aMatrix._42;
  matrix._13 = _11 * aMatrix._13 + _12 * aMatrix._23 + _13 * aMatrix._33 + _14 * aMatrix._43;
  matrix._23 = _21 * aMatrix._13 + _22 * aMatrix._23 + _23 * aMatrix._33 + _24 * aMatrix._43;
  matrix._33 = _31 * aMatrix._13 + _32 * aMatrix._23 + _33 * aMatrix._33 + _34 * aMatrix._43;
  matrix._43 = _41 * aMatrix._13 + _42 * aMatrix._23 + _43 * aMatrix._33 + _44 * aMatrix._43;
  matrix._14 = _11 * aMatrix._14 + _12 * aMatrix._24 + _13 * aMatrix._34 + _14 * aMatrix._44;
  matrix._24 = _21 * aMatrix._14 + _22 * aMatrix._24 + _23 * aMatrix._34 + _24 * aMatrix._44;
  matrix._34 = _31 * aMatrix._14 + _32 * aMatrix._24 + _33 * aMatrix._34 + _34 * aMatrix._44;
  matrix._44 = _41 * aMatrix._14 + _42 * aMatrix._24 + _43 * aMatrix._34 + _44 * aMatrix._44;

  return matrix;
}
Example #9
0
void AsyncPanZoomController::GetContentTransformForFrame(const FrameMetrics& aFrame,
                                                         const gfx3DMatrix& aRootTransform,
                                                         const gfxSize& aWidgetSize,
                                                         gfx3DMatrix* aTreeTransform,
                                                         gfxPoint* aReverseViewTranslation) {
  // Scales on the root layer, on what's currently painted.
  float rootScaleX = aRootTransform.GetXScale(),
        rootScaleY = aRootTransform.GetYScale();

  // Current local transform; this is not what's painted but rather what PZC has
  // transformed due to touches like panning or pinching. Eventually, the root
  // layer transform will become this during runtime, but we must wait for Gecko
  // to repaint.
  float localScaleX = mFrameMetrics.mResolution.width,
        localScaleY = mFrameMetrics.mResolution.height;

  // Handle transformations for asynchronous panning and zooming. We determine the
  // zoom used by Gecko from the transformation set on the root layer, and we
  // determine the scroll offset used by Gecko from the frame metrics of the
  // primary scrollable layer. We compare this to the desired zoom and scroll
  // offset in the view transform we obtained from Java in order to compute the
  // transformation we need to apply.
  float tempScaleDiffX = rootScaleX * localScaleX;
  float tempScaleDiffY = rootScaleY * localScaleY;

  nsIntPoint metricsScrollOffset(0, 0);
  if (aFrame.IsScrollable())
    metricsScrollOffset = aFrame.mViewportScrollOffset;

  nsIntPoint scrollCompensation(
    mFrameMetrics.mViewportScrollOffset.x / rootScaleX - metricsScrollOffset.x,
    mFrameMetrics.mViewportScrollOffset.y / rootScaleY - metricsScrollOffset.y);

  ViewTransform treeTransform(-scrollCompensation, localScaleX, localScaleY);
  *aTreeTransform = gfx3DMatrix(treeTransform);

  float offsetX = mFrameMetrics.mViewportScrollOffset.x / tempScaleDiffX,
        offsetY = mFrameMetrics.mViewportScrollOffset.y / tempScaleDiffY;

  nsIntRect localContentRect = mFrameMetrics.mContentRect;
  offsetX = NS_MAX((float)localContentRect.x,
                   NS_MIN(offsetX, (float)(localContentRect.XMost() - aWidgetSize.width)));
  offsetY = NS_MAX((float)localContentRect.y,
                    NS_MIN(offsetY, (float)(localContentRect.YMost() - aWidgetSize.height)));
  *aReverseViewTranslation = gfxPoint(offsetX - metricsScrollOffset.x,
                                      offsetY - metricsScrollOffset.y);
}
/* Helper function to set up a scale matrix. */
static void
ProcessScaleHelper(gfx3DMatrix& aMatrix,
                   float aXScale, 
                   float aYScale, 
                   float aZScale)
{
  aMatrix.Scale(aXScale, aYScale, aZScale);
}
static void 
ProcessPerspective(gfx3DMatrix& aMatrix, 
                   const nsCSSValue::Array* aData,
                   nsStyleContext *aContext,
                   nsPresContext *aPresContext,
                   bool &aCanStoreInRuleTree)
{
  NS_PRECONDITION(aData->Count() == 2, "Invalid array!");

  float depth = ProcessTranslatePart(aData->Item(1), aContext,
                                     aPresContext, aCanStoreInRuleTree,
                                     0);
  aMatrix.Perspective(depth);
}
static void 
ProcessTranslateZ(gfx3DMatrix& aMatrix,
                  const nsCSSValue::Array* aData,
                  nsStyleContext* aContext,
                  nsPresContext* aPresContext,
                  bool& aCanStoreInRuleTree)
{
  NS_PRECONDITION(aData->Count() == 2, "Invalid array!");

  Point3D temp;

  temp.z = ProcessTranslatePart(aData->Item(1), aContext,
                                aPresContext, aCanStoreInRuleTree, 0);
  aMatrix.Translate(temp);
}
Example #13
0
gfx3DMatrix
Layer::SnapTransform(const gfx3DMatrix& aTransform,
                     const gfxRect& aSnapRect,
                     gfxMatrix* aResidualTransform)
{
  if (aResidualTransform) {
    *aResidualTransform = gfxMatrix();
  }

  gfxMatrix matrix2D;
  gfx3DMatrix result;
  if (mManager->IsSnappingEffectiveTransforms() &&
      aTransform.Is2D(&matrix2D) &&
      matrix2D.HasNonIntegerTranslation() &&
      !matrix2D.IsSingular() &&
      !matrix2D.HasNonAxisAlignedTransform()) {
    gfxMatrix snappedMatrix;
    gfxPoint topLeft = matrix2D.Transform(aSnapRect.TopLeft());
    topLeft.Round();
    // first compute scale factors that scale aSnapRect to the snapped rect
    if (aSnapRect.IsEmpty()) {
      snappedMatrix.xx = matrix2D.xx;
      snappedMatrix.yy = matrix2D.yy;
    } else {
      gfxPoint bottomRight = matrix2D.Transform(aSnapRect.BottomRight());
      bottomRight.Round();
      snappedMatrix.xx = (bottomRight.x - topLeft.x)/aSnapRect.Width();
      snappedMatrix.yy = (bottomRight.y - topLeft.y)/aSnapRect.Height();
    }
    // compute translation factors that will move aSnapRect to the snapped rect
    // given those scale factors
    snappedMatrix.x0 = topLeft.x - aSnapRect.X()*snappedMatrix.xx;
    snappedMatrix.y0 = topLeft.y - aSnapRect.Y()*snappedMatrix.yy;
    result = gfx3DMatrix::From2D(snappedMatrix);
    if (aResidualTransform && !snappedMatrix.IsSingular()) {
      // set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
      // (i.e., appying snappedMatrix after aResidualTransform gives the
      // ideal transform.
      gfxMatrix snappedMatrixInverse = snappedMatrix;
      snappedMatrixInverse.Invert();
      *aResidualTransform = matrix2D * snappedMatrixInverse;
    }
  } else {
    result = aTransform;
  }
  return result;
}
static nsIntRect
TransformRect(const nsIntRect& aRect, const gfx3DMatrix& aTransform)
{
    if (aRect.IsEmpty()) {
        return nsIntRect();
    }

    gfxRect rect(aRect.x, aRect.y, aRect.width, aRect.height);
    rect = aTransform.TransformBounds(rect);
    rect.RoundOut();

    nsIntRect intRect;
    if (!gfxUtils::GfxRectToIntRect(rect, &intRect)) {
        return nsIntRect();
    }

    return intRect;
}
static void
SubtractTransformedRegion(nsIntRegion& aRegion,
                          const nsIntRegion& aRegionToSubtract,
                          const gfx3DMatrix& aTransform)
{
  if (aRegionToSubtract.IsEmpty()) {
    return;
  }

  // For each rect in the region, find out its bounds in screen space and
  // subtract it from the screen region.
  nsIntRegionRectIterator it(aRegionToSubtract);
  while (const nsIntRect* rect = it.Next()) {
    gfxRect incompleteRect = aTransform.TransformBounds(gfxRect(*rect));
    aRegion.Sub(aRegion, nsIntRect(incompleteRect.x,
                                   incompleteRect.y,
                                   incompleteRect.width,
                                   incompleteRect.height));
  }
}
Example #16
0
static void
Transform(DataSourceSurface* aDest,
          DataSourceSurface* aSource,
          const gfx3DMatrix& aTransform,
          const Point& aDestOffset)
{
  if (aTransform.IsSingular()) {
    return;
  }

  IntSize destSize = aDest->GetSize();
  SkImageInfo destInfo = SkImageInfo::Make(destSize.width,
                                           destSize.height,
                                           kBGRA_8888_SkColorType,
                                           kPremul_SkAlphaType);
  SkBitmap destBitmap;
  destBitmap.setInfo(destInfo, aDest->Stride());
  destBitmap.setPixels((uint32_t*)aDest->GetData());
  SkCanvas destCanvas(destBitmap);

  IntSize srcSize = aSource->GetSize();
  SkImageInfo srcInfo = SkImageInfo::Make(srcSize.width,
                                          srcSize.height,
                                          kBGRA_8888_SkColorType,
                                          kPremul_SkAlphaType);
  SkBitmap src;
  src.setInfo(srcInfo, aSource->Stride());
  src.setPixels((uint32_t*)aSource->GetData());

  gfx3DMatrix transform = aTransform;
  transform.TranslatePost(Point3D(-aDestOffset.x, -aDestOffset.y, 0));
  destCanvas.setMatrix(Matrix3DToSkia(transform));

  SkPaint paint;
  paint.setXfermodeMode(SkXfermode::kSrc_Mode);
  paint.setAntiAlias(true);
  paint.setFilterLevel(SkPaint::kLow_FilterLevel);
  SkRect destRect = SkRect::MakeXYWH(0, 0, srcSize.width, srcSize.height);
  destCanvas.drawBitmapRectToRect(src, nullptr, destRect, &paint);
}
Example #17
0
gfx3DMatrix
Layer::SnapTransform(const gfx3DMatrix& aTransform,
                     const gfxRect& aSnapRect,
                     gfxMatrix* aResidualTransform)
{
  if (aResidualTransform) {
    *aResidualTransform = gfxMatrix();
  }

  gfxMatrix matrix2D;
  gfx3DMatrix result;
  if (!(mContentFlags & CONTENT_DISABLE_TRANSFORM_SNAPPING) &&
      mManager->IsSnappingEffectiveTransforms() &&
      aTransform.Is2D(&matrix2D) &&
      gfxSize(1.0, 1.0) <= aSnapRect.Size() &&
      matrix2D.PreservesAxisAlignedRectangles()) {
    gfxPoint transformedTopLeft = matrix2D.Transform(aSnapRect.TopLeft());
    transformedTopLeft.Round();
    gfxPoint transformedTopRight = matrix2D.Transform(aSnapRect.TopRight());
    transformedTopRight.Round();
    gfxPoint transformedBottomRight = matrix2D.Transform(aSnapRect.BottomRight());
    transformedBottomRight.Round();

    gfxMatrix snappedMatrix = gfxUtils::TransformRectToRect(aSnapRect,
      transformedTopLeft, transformedTopRight, transformedBottomRight);

    result = gfx3DMatrix::From2D(snappedMatrix);
    if (aResidualTransform && !snappedMatrix.IsSingular()) {
      // set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
      // (i.e., appying snappedMatrix after aResidualTransform gives the
      // ideal transform.
      gfxMatrix snappedMatrixInverse = snappedMatrix;
      snappedMatrixInverse.Invert();
      *aResidualTransform = matrix2D * snappedMatrixInverse;
    }
  } else {
    result = aTransform;
  }
  return result;
}
static void 
ProcessMatrix3D(gfx3DMatrix& aMatrix,
                const nsCSSValue::Array* aData,
                nsStyleContext* aContext,
                nsPresContext* aPresContext,
                bool& aCanStoreInRuleTree,
                nsRect& aBounds)
{
  NS_PRECONDITION(aData->Count() == 17, "Invalid array!");

  gfx3DMatrix temp;

  temp._11 = aData->Item(1).GetFloatValue();
  temp._12 = aData->Item(2).GetFloatValue();
  temp._13 = aData->Item(3).GetFloatValue();
  temp._14 = aData->Item(4).GetFloatValue();
  temp._21 = aData->Item(5).GetFloatValue();
  temp._22 = aData->Item(6).GetFloatValue();
  temp._23 = aData->Item(7).GetFloatValue();
  temp._24 = aData->Item(8).GetFloatValue();
  temp._31 = aData->Item(9).GetFloatValue();
  temp._32 = aData->Item(10).GetFloatValue();
  temp._33 = aData->Item(11).GetFloatValue();
  temp._34 = aData->Item(12).GetFloatValue();
  temp._44 = aData->Item(16).GetFloatValue();

  temp._41 = ProcessTranslatePart(aData->Item(13),
                                  aContext, aPresContext, aCanStoreInRuleTree,
                                  aBounds.Width());
  temp._42 = ProcessTranslatePart(aData->Item(14),
                                  aContext, aPresContext, aCanStoreInRuleTree,
                                  aBounds.Height());
  temp._43 = ProcessTranslatePart(aData->Item(15),
                                  aContext, aPresContext, aCanStoreInRuleTree,
                                  aBounds.Height());

  aMatrix.PreMultiply(temp);
}
static LayoutDeviceRect
TransformCompositionBounds(const ParentLayerRect& aCompositionBounds,
                           const CSSToParentLayerScale& aZoom,
                           const ScreenPoint& aScrollOffset,
                           const CSSToScreenScale& aResolution,
                           const gfx3DMatrix& aTransformScreenToLayout)
{
  // Transform the current composition bounds into transformed layout device
  // space by compensating for the difference in resolution and subtracting the
  // old composition bounds origin.
  ScreenRect offsetViewportRect = (aCompositionBounds / aZoom) * aResolution;
  offsetViewportRect.MoveBy(-aScrollOffset);

  gfxRect transformedViewport =
    aTransformScreenToLayout.TransformBounds(
      gfxRect(offsetViewportRect.x, offsetViewportRect.y,
              offsetViewportRect.width, offsetViewportRect.height));

  return LayoutDeviceRect(transformedViewport.x,
                          transformedViewport.y,
                          transformedViewport.width,
                          transformedViewport.height);
}
static float
GetDisplayportCoverage(const gfx::Rect& aDisplayPort,
                       const gfx3DMatrix& aTransformToScreen,
                       const nsIntRect& aScreenRect)
{
  gfxRect transformedDisplayport =
    aTransformToScreen.TransformBounds(gfxRect(aDisplayPort.x,
                                               aDisplayPort.y,
                                               aDisplayPort.width,
                                               aDisplayPort.height));
  transformedDisplayport.RoundOut();
  nsIntRect displayport = nsIntRect(transformedDisplayport.x,
                                    transformedDisplayport.y,
                                    transformedDisplayport.width,
                                    transformedDisplayport.height);
  if (!displayport.Contains(aScreenRect)) {
    nsIntRegion coveredRegion;
    coveredRegion.And(aScreenRect, displayport);
    return GetRegionArea(coveredRegion) / (float)(aScreenRect.width * aScreenRect.height);
  }

  return 1.0f;
}
/* Helper function to process a translate function. */
static void
ProcessTranslate(gfx3DMatrix& aMatrix,
                 const nsCSSValue::Array* aData,
                 nsStyleContext* aContext,
                 nsPresContext* aPresContext,
                 bool& aCanStoreInRuleTree,
                 nsRect& aBounds)
{
  NS_PRECONDITION(aData->Count() == 2 || aData->Count() == 3, "Invalid array!");

  Point3D temp;

  temp.x = ProcessTranslatePart(aData->Item(1),
                                aContext, aPresContext, aCanStoreInRuleTree,
                                aBounds.Width());

  /* If we read in a Y component, set it appropriately */
  if (aData->Count() == 3) {
    temp.y = ProcessTranslatePart(aData->Item(2),
                                  aContext, aPresContext, aCanStoreInRuleTree,
                                  aBounds.Height());
  }
  aMatrix.Translate(temp);
}
/* Helper function that, given a set of angles, constructs the appropriate
 * skew matrix.
 */
static void
ProcessSkewHelper(gfx3DMatrix& aMatrix, double aXAngle, double aYAngle)
{
  aMatrix.SkewXY(aXAngle, aYAngle);
}
void
AsyncCompositionManager::AlignFixedLayersForAnchorPoint(Layer* aLayer,
                                                        Layer* aTransformedSubtreeRoot,
                                                        const gfx3DMatrix& aPreviousTransformForRoot,
                                                        const LayerMargin& aFixedLayerMargins)
{
  if (aLayer != aTransformedSubtreeRoot && aLayer->GetIsFixedPosition() &&
      !aLayer->GetParent()->GetIsFixedPosition()) {
    // Insert a translation so that the position of the anchor point is the same
    // before and after the change to the transform of aTransformedSubtreeRoot.
    // This currently only works for fixed layers with 2D transforms.

    // Accumulate the transforms between this layer and the subtree root layer.
    gfxMatrix ancestorTransform;
    if (!AccumulateLayerTransforms2D(aLayer->GetParent(), aTransformedSubtreeRoot,
                                     ancestorTransform)) {
      return;
    }

    gfxMatrix oldRootTransform;
    gfxMatrix newRootTransform;
    if (!aPreviousTransformForRoot.Is2D(&oldRootTransform) ||
        !aTransformedSubtreeRoot->GetLocalTransform().Is2D(&newRootTransform)) {
      return;
    }

    // Calculate the cumulative transforms between the subtree root with the
    // old transform and the current transform.
    gfxMatrix oldCumulativeTransform = ancestorTransform * oldRootTransform;
    gfxMatrix newCumulativeTransform = ancestorTransform * newRootTransform;
    if (newCumulativeTransform.IsSingular()) {
      return;
    }
    gfxMatrix newCumulativeTransformInverse = newCumulativeTransform;
    newCumulativeTransformInverse.Invert();

    // Now work out the translation necessary to make sure the layer doesn't
    // move given the new sub-tree root transform.
    gfxMatrix layerTransform;
    if (!GetBaseTransform2D(aLayer, &layerTransform)) {
      return;
    }

    // Calculate any offset necessary, in previous transform sub-tree root
    // space. This is used to make sure fixed position content respects
    // content document fixed position margins.
    LayerPoint offsetInOldSubtreeLayerSpace = GetLayerFixedMarginsOffset(aLayer, aFixedLayerMargins);

    // Add the above offset to the anchor point so we can offset the layer by
    // and amount that's specified in old subtree layer space.
    const LayerPoint& anchorInOldSubtreeLayerSpace = aLayer->GetFixedPositionAnchor();
    LayerPoint offsetAnchorInOldSubtreeLayerSpace = anchorInOldSubtreeLayerSpace + offsetInOldSubtreeLayerSpace;

    // Add the local layer transform to the two points to make the equation
    // below this section more convenient.
    gfxPoint anchor(anchorInOldSubtreeLayerSpace.x, anchorInOldSubtreeLayerSpace.y);
    gfxPoint offsetAnchor(offsetAnchorInOldSubtreeLayerSpace.x, offsetAnchorInOldSubtreeLayerSpace.y);
    gfxPoint locallyTransformedAnchor = layerTransform.Transform(anchor);
    gfxPoint locallyTransformedOffsetAnchor = layerTransform.Transform(offsetAnchor);

    // Transforming the locallyTransformedAnchor by oldCumulativeTransform
    // returns the layer's anchor point relative to the parent of
    // aTransformedSubtreeRoot, before the new transform was applied.
    // Then, applying newCumulativeTransformInverse maps that point relative
    // to the layer's parent, which is the same coordinate space as
    // locallyTransformedAnchor again, allowing us to subtract them and find
    // out the offset necessary to make sure the layer stays stationary.
    gfxPoint oldAnchorPositionInNewSpace =
      newCumulativeTransformInverse.Transform(
        oldCumulativeTransform.Transform(locallyTransformedOffsetAnchor));
    gfxPoint translation = oldAnchorPositionInNewSpace - locallyTransformedAnchor;

    // Finally, apply the 2D translation to the layer transform.
    TranslateShadowLayer2D(aLayer, translation);

    // The transform has now been applied, so there's no need to iterate over
    // child layers.
    return;
  }

  for (Layer* child = aLayer->GetFirstChild();
       child; child = child->GetNextSibling()) {
    AlignFixedLayersForAnchorPoint(child, aTransformedSubtreeRoot,
                                   aPreviousTransformForRoot, aFixedLayerMargins);
  }
}
void 
LayerPropertiesBase::MoveBy(const nsIntPoint& aOffset)
{
  mTransform.TranslatePost(gfxPoint3D(aOffset.x, aOffset.y, 0)); 
}