void BasicContainerLayer::ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface) { // 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; idealTransform.ProjectTo2D(); if (!idealTransform.CanDraw2D()) { mEffectiveTransform = idealTransform; ComputeEffectiveTransformsForChildren(gfx3DMatrix()); ComputeEffectiveTransformForMaskLayer(gfx3DMatrix()); mUseIntermediateSurface = true; return; } mEffectiveTransform = SnapTransformTranslation(idealTransform, &residual); // We always pass the ideal matrix down to our children, so there is no // need to apply any compensation using the residual from SnapTransformTranslation. ComputeEffectiveTransformsForChildren(idealTransform); ComputeEffectiveTransformForMaskLayer(aTransformToSurface); /* If we have a single child, it can just inherit our opacity, * otherwise we need a PushGroup and we need to mark ourselves as using * an intermediate surface so our children don't inherit our opacity * via GetEffectiveOpacity. * Having a mask layer always forces our own push group */ mUseIntermediateSurface = GetMaskLayer() || (GetEffectiveOpacity() != 1.0 && HasMultipleChildren()); }
// Go down shadow layer tree and apply transformations for scrollable layers. static void TransformShadowTree(nsDisplayListBuilder* aBuilder, nsFrameLoader* aFrameLoader, nsIFrame* aFrame, Layer* aLayer, const ViewTransform& aTransform) { ShadowLayer* shadow = aLayer->AsShadowLayer(); shadow->SetShadowClipRect(aLayer->GetClipRect()); shadow->SetShadowVisibleRegion(aLayer->GetVisibleRegion()); const FrameMetrics* metrics = GetFrameMetrics(aLayer); gfx3DMatrix shadowTransform; ViewTransform layerTransform = aTransform; if (metrics && metrics->IsScrollable()) { const ViewID scrollId = metrics->mScrollId; const nsContentView* view = aFrameLoader->GetCurrentRemoteFrame()->GetContentView(scrollId); NS_ABORT_IF_FALSE(view, "Array of views should be consistent with layer tree"); const gfx3DMatrix& currentTransform = aLayer->GetTransform(); ViewTransform viewTransform = ComputeShadowTreeTransform( aFrame, aFrameLoader, metrics, view->GetViewConfig(), 1 / (GetXScale(currentTransform)*layerTransform.mXScale), 1 / (GetYScale(currentTransform)*layerTransform.mYScale) ); // Apply the layer's own transform *before* the view transform shadowTransform = gfx3DMatrix(viewTransform) * currentTransform; if (metrics->IsRootScrollable()) { layerTransform.mTranslation = viewTransform.mTranslation; // Apply the root frame translation *before* we do the rest of the transforms. nsIntPoint rootFrameOffset = GetRootFrameOffset(aFrame, aBuilder); shadowTransform = shadowTransform * gfx3DMatrix::Translation(float(rootFrameOffset.x), float(rootFrameOffset.y), 0.0); layerTransform.mXScale *= GetXScale(currentTransform); layerTransform.mYScale *= GetYScale(currentTransform); } } else { shadowTransform = aLayer->GetTransform(); } if (aLayer->GetIsFixedPosition() && !aLayer->GetParent()->GetIsFixedPosition()) { ReverseTranslate(shadowTransform, layerTransform); const nsIntRect* clipRect = shadow->GetShadowClipRect(); if (clipRect) { nsIntRect transformedClipRect(*clipRect); transformedClipRect.MoveBy(shadowTransform._41, shadowTransform._42); shadow->SetShadowClipRect(&transformedClipRect); } } shadow->SetShadowTransform(shadowTransform); for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { TransformShadowTree(aBuilder, aFrameLoader, aFrame, child, layerTransform); } }
void LayerManagerD3D9::EndTransaction(DrawThebesLayerCallback aCallback, void* aCallbackData, EndTransactionFlags aFlags) { mDeviceResetCount = mDeviceManager->GetDeviceResetCount(); if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) { mCurrentCallbackInfo.Callback = aCallback; mCurrentCallbackInfo.CallbackData = aCallbackData; // The results of our drawing always go directly into a pixel buffer, // so we don't need to pass any global transform here. mRoot->ComputeEffectiveTransforms(gfx3DMatrix()); SetCompositingDisabled(aFlags & END_NO_COMPOSITE); Render(); /* Clean this out for sanity */ mCurrentCallbackInfo.Callback = NULL; mCurrentCallbackInfo.CallbackData = NULL; } // Clear mTarget, next transaction could have no target mTarget = NULL; }
void LayerManagerD3D10::EndTransaction(DrawThebesLayerCallback aCallback, void* aCallbackData, EndTransactionFlags aFlags) { if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) { mCurrentCallbackInfo.Callback = aCallback; mCurrentCallbackInfo.CallbackData = aCallbackData; // The results of our drawing always go directly into a pixel buffer, // so we don't need to pass any global transform here. mRoot->ComputeEffectiveTransforms(gfx3DMatrix()); #ifdef MOZ_LAYERS_HAVE_LOG MOZ_LAYERS_LOG((" ----- (beginning paint)")); Log(); #endif Render(); mCurrentCallbackInfo.Callback = nsnull; mCurrentCallbackInfo.CallbackData = nsnull; } #ifdef MOZ_LAYERS_HAVE_LOG Log(); MOZ_LAYERS_LOG(("]----- EndTransaction")); #endif mTarget = nsnull; }
void LayerManagerD3D9::EndTransaction(DrawThebesLayerCallback aCallback, void* aCallbackData, EndTransactionFlags aFlags) { mInTransaction = false; mDeviceResetCount = mDeviceManager->GetDeviceResetCount(); if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) { mCurrentCallbackInfo.Callback = aCallback; mCurrentCallbackInfo.CallbackData = aCallbackData; if (aFlags & END_NO_COMPOSITE) { // Apply pending tree updates before recomputing effective // properties. mRoot->ApplyPendingUpdatesToSubtree(); } // The results of our drawing always go directly into a pixel buffer, // so we don't need to pass any global transform here. mRoot->ComputeEffectiveTransforms(gfx3DMatrix()); SetCompositingDisabled(aFlags & END_NO_COMPOSITE); Render(); /* Clean this out for sanity */ mCurrentCallbackInfo.Callback = nullptr; mCurrentCallbackInfo.CallbackData = nullptr; } // Clear mTarget, next transaction could have no target mTarget = nullptr; }
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; }
/* static */ gfx3DMatrix nsStyleTransformMatrix::ProcessRotate3D(const nsCSSValue::Array* aData) { NS_PRECONDITION(aData->Count() == 5, "Invalid array!"); /* We want our matrix to look like this: * | 1 + (1-cos(angle))*(x*x-1) -z*sin(angle)+(1-cos(angle))*x*y y*sin(angle)+(1-cos(angle))*x*z 0 | * | z*sin(angle)+(1-cos(angle))*x*y 1 + (1-cos(angle))*(y*y-1) -x*sin(angle)+(1-cos(angle))*y*z 0 | * | -y*sin(angle)+(1-cos(angle))*x*z x*sin(angle)+(1-cos(angle))*y*z 1 + (1-cos(angle))*(z*z-1) 0 | * | 0 0 0 1 | * (see http://www.w3.org/TR/css3-3d-transforms/#transform-functions) */ double theta = aData->Item(4).GetAngleValueInRadians(); float cosTheta = FlushToZero(cos(theta)); float sinTheta = FlushToZero(sin(theta)); float x = aData->Item(1).GetFloatValue(); float y = aData->Item(2).GetFloatValue(); float z = aData->Item(3).GetFloatValue(); /* Normalize [x,y,z] */ float length = sqrt(x*x + y*y + z*z); if (length == 0.0) { return gfx3DMatrix(); } x /= length; y /= length; z /= length; gfx3DMatrix temp; /* Create our matrix */ temp._11 = 1 + (1 - cosTheta) * (x * x - 1); temp._12 = -z * sinTheta + (1 - cosTheta) * x * y; temp._13 = y * sinTheta + (1 - cosTheta) * x * z; temp._14 = 0.0f; temp._21 = z * sinTheta + (1 - cosTheta) * x * y; temp._22 = 1 + (1 - cosTheta) * (y * y - 1); temp._23 = -x * sinTheta + (1 - cosTheta) * y * z; temp._24 = 0.0f; temp._31 = -y * sinTheta + (1 - cosTheta) * x * z; temp._32 = x * sinTheta + (1 - cosTheta) * y * z; temp._33 = 1 + (1 - cosTheta) * (z * z - 1); temp._34 = 0.0f; temp._41 = 0.0f; temp._42 = 0.0f; temp._43 = 0.0f; temp._44 = 1.0f; return temp; }
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); }
bool ClientLayerManager::EndTransactionInternal(DrawThebesLayerCallback aCallback, void* aCallbackData, EndTransactionFlags) { PROFILER_LABEL("ClientLayerManager", "EndTransactionInternal"); #ifdef MOZ_LAYERS_HAVE_LOG MOZ_LAYERS_LOG((" ----- (beginning paint)")); Log(); #endif profiler_tracing("Paint", "Rasterize", TRACING_INTERVAL_START); NS_ASSERTION(InConstruction(), "Should be in construction phase"); mPhase = PHASE_DRAWING; ClientLayer* root = ClientLayer::ToClientLayer(GetRoot()); mTransactionIncomplete = false; // Apply pending tree updates before recomputing effective // properties. GetRoot()->ApplyPendingUpdatesToSubtree(); mThebesLayerCallback = aCallback; mThebesLayerCallbackData = aCallbackData; GetRoot()->ComputeEffectiveTransforms(gfx3DMatrix()); root->RenderLayer(); mThebesLayerCallback = nullptr; mThebesLayerCallbackData = nullptr; // Go back to the construction phase if the transaction isn't complete. // Layout will update the layer tree and call EndTransaction(). mPhase = mTransactionIncomplete ? PHASE_CONSTRUCTION : PHASE_NONE; NS_ASSERTION(!aCallback || !mTransactionIncomplete, "If callback is not null, transaction must be complete"); return !mTransactionIncomplete; }
void LayerManagerOGL::EndTransaction(DrawThebesLayerCallback aCallback, void* aCallbackData, EndTransactionFlags aFlags) { #ifdef MOZ_LAYERS_HAVE_LOG MOZ_LAYERS_LOG((" ----- (beginning paint)")); Log(); #endif if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return; } if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) { // The results of our drawing always go directly into a pixel buffer, // so we don't need to pass any global transform here. mRoot->ComputeEffectiveTransforms(gfx3DMatrix()); mThebesLayerCallback = aCallback; mThebesLayerCallbackData = aCallbackData; Render(); mThebesLayerCallback = nsnull; mThebesLayerCallbackData = nsnull; } mTarget = NULL; #ifdef MOZ_LAYERS_HAVE_LOG Log(); MOZ_LAYERS_LOG(("]----- EndTransaction")); #endif }
static void RenderColorLayer(ColorLayer* aLayer, LayerManagerOGL *aManager, const nsIntPoint& aOffset) { if (aManager->CompositingDisabled()) { return; } aManager->MakeCurrent(); // XXX we might be able to improve performance by using glClear /* Multiply color by the layer opacity, as the shader * ignores layer opacity and expects a final color to * write to the color buffer. This saves a needless * multiply in the fragment shader. */ gfxRGBA color(aLayer->GetColor()); float opacity = aLayer->GetEffectiveOpacity() * color.a; color.r *= opacity; color.g *= opacity; color.b *= opacity; color.a = opacity; ShaderProgramOGL *program = aManager->GetProgram(ColorLayerProgramType, aLayer->GetMaskLayer()); program->Activate(); program->SetLayerQuadRect(aLayer->GetBounds()); program->SetLayerTransform(aLayer->GetEffectiveTransform()); program->SetTextureTransform(gfx3DMatrix()); program->SetRenderOffset(aOffset); program->SetRenderColor(color); program->LoadMask(aLayer->GetMaskLayer()); aManager->BindAndDrawQuad(program); }
/** * SetToTransformFunction is essentially a giant switch statement that fans * out to many smaller helper functions. */ gfx3DMatrix nsStyleTransformMatrix::MatrixForTransformFunction(const nsCSSValue::Array * aData, nsStyleContext* aContext, nsPresContext* aPresContext, PRBool& aCanStoreInRuleTree, nsRect& aBounds, float aAppUnitsPerMatrixUnit) { NS_PRECONDITION(aData, "Why did you want to get data from a null array?"); // It's OK if aContext and aPresContext are null if the caller already // knows that all length units have been converted to pixels (as // nsStyleAnimation does). /* Get the keyword for the transform. */ switch (TransformFunctionOf(aData)) { case eCSSKeyword_translatex: return ProcessTranslateX(aData, aContext, aPresContext, aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit); case eCSSKeyword_translatey: return ProcessTranslateY(aData, aContext, aPresContext, aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit); case eCSSKeyword_translatez: return ProcessTranslateZ(aData, aContext, aPresContext, aCanStoreInRuleTree, aAppUnitsPerMatrixUnit); case eCSSKeyword_translate: return ProcessTranslate(aData, aContext, aPresContext, aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit); case eCSSKeyword_translate3d: return ProcessTranslate3D(aData, aContext, aPresContext, aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit); case eCSSKeyword_scalex: return ProcessScaleX(aData); case eCSSKeyword_scaley: return ProcessScaleY(aData); case eCSSKeyword_scalez: return ProcessScaleZ(aData); case eCSSKeyword_scale: return ProcessScale(aData); case eCSSKeyword_scale3d: return ProcessScale3D(aData); case eCSSKeyword_skewx: return ProcessSkewX(aData); case eCSSKeyword_skewy: return ProcessSkewY(aData); case eCSSKeyword_skew: return ProcessSkew(aData); case eCSSKeyword_rotatex: return ProcessRotateX(aData); case eCSSKeyword_rotatey: return ProcessRotateY(aData); case eCSSKeyword_rotatez: case eCSSKeyword_rotate: return ProcessRotateZ(aData); case eCSSKeyword_rotate3d: return ProcessRotate3D(aData); case eCSSKeyword_matrix: return ProcessMatrix(aData, aContext, aPresContext, aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit); case eCSSKeyword_matrix3d: return ProcessMatrix3D(aData); case eCSSKeyword_interpolatematrix: return ProcessInterpolateMatrix(aData, aContext, aPresContext, aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit); case eCSSKeyword_perspective: return ProcessPerspective(aData, aContext, aPresContext, aCanStoreInRuleTree, aAppUnitsPerMatrixUnit); default: NS_NOTREACHED("Unknown transform function!"); } return gfx3DMatrix(); }
void AsyncCompositionManager::TransformScrollableLayer(Layer* aLayer, const LayoutDeviceToLayerScale& aResolution) { LayerComposite* layerComposite = aLayer->AsLayerComposite(); ContainerLayer* container = aLayer->AsContainerLayer(); const FrameMetrics& metrics = container->GetFrameMetrics(); // We must apply the resolution scale before a pan/zoom transform, so we call // GetTransform here. const gfx3DMatrix& currentTransform = aLayer->GetTransform(); gfx3DMatrix oldTransform = currentTransform; gfx3DMatrix treeTransform; CSSToLayerScale geckoZoom = metrics.mDevPixelsPerCSSPixel * aResolution; LayerIntPoint scrollOffsetLayerPixels = RoundedToInt(metrics.mScrollOffset * geckoZoom); if (mIsFirstPaint) { mContentRect = metrics.mScrollableRect; SetFirstPaintViewport(scrollOffsetLayerPixels, geckoZoom, mContentRect); mIsFirstPaint = false; } else if (!metrics.mScrollableRect.IsEqualEdges(mContentRect)) { mContentRect = metrics.mScrollableRect; SetPageRect(mContentRect); } // We synchronise the viewport information with Java after sending the above // notifications, so that Java can take these into account in its response. // Calculate the absolute display port to send to Java LayerIntRect displayPort = RoundedToInt( (metrics.mCriticalDisplayPort.IsEmpty() ? metrics.mDisplayPort : metrics.mCriticalDisplayPort ) * geckoZoom); displayPort += scrollOffsetLayerPixels; LayerMargin fixedLayerMargins(0, 0, 0, 0); ScreenPoint offset(0, 0); // Ideally we would initialize userZoom to AsyncPanZoomController::CalculateResolution(metrics) // but this causes a reftest-ipc test to fail (see bug 883646 comment 27). The reason for this // appears to be that metrics.mZoom is poorly initialized in some scenarios. In these scenarios, // however, we can assume there is no async zooming in progress and so the following statement // works fine. CSSToScreenScale userZoom(metrics.mDevPixelsPerCSSPixel.scale * metrics.mResolution.scale); ScreenPoint userScroll = metrics.mScrollOffset * userZoom; SyncViewportInfo(displayPort, geckoZoom, mLayersUpdated, userScroll, userZoom, fixedLayerMargins, offset); mLayersUpdated = false; // Apply the render offset mLayerManager->GetCompositor()->SetScreenRenderOffset(offset); // 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 user zoom and scroll // offset in the view transform we obtained from Java in order to compute the // transformation we need to apply. LayerToScreenScale zoomAdjust = userZoom / geckoZoom; LayerIntPoint geckoScroll(0, 0); if (metrics.IsScrollable()) { geckoScroll = scrollOffsetLayerPixels; } LayerPoint translation = (userScroll / zoomAdjust) - geckoScroll; treeTransform = gfx3DMatrix(ViewTransform(-translation, userZoom / metrics.mDevPixelsPerCSSPixel)); // The transform already takes the resolution scale into account. Since we // will apply the resolution scale again when computing the effective // transform, we must apply the inverse resolution scale here. gfx3DMatrix computedTransform = treeTransform * currentTransform; computedTransform.Scale(1.0f/container->GetPreXScale(), 1.0f/container->GetPreYScale(), 1); computedTransform.ScalePost(1.0f/container->GetPostXScale(), 1.0f/container->GetPostYScale(), 1); layerComposite->SetShadowTransform(computedTransform); NS_ASSERTION(!layerComposite->GetShadowTransformSetByAnimation(), "overwriting animated transform!"); // Apply resolution scaling to the old transform - the layer tree as it is // doesn't have the necessary transform to display correctly. oldTransform.Scale(aResolution.scale, aResolution.scale, 1); // Make sure that overscroll and under-zoom are represented in the old // transform so that fixed position content moves and scales accordingly. // These calculations will effectively scale and offset fixed position layers // in screen space when the compensatory transform is performed in // AlignFixedLayersForAnchorPoint. ScreenRect contentScreenRect = mContentRect * userZoom; gfxPoint3D overscrollTranslation; if (userScroll.x < contentScreenRect.x) { overscrollTranslation.x = contentScreenRect.x - userScroll.x; } else if (userScroll.x + metrics.mCompositionBounds.width > contentScreenRect.XMost()) { overscrollTranslation.x = contentScreenRect.XMost() - (userScroll.x + metrics.mCompositionBounds.width); } if (userScroll.y < contentScreenRect.y) { overscrollTranslation.y = contentScreenRect.y - userScroll.y; } else if (userScroll.y + metrics.mCompositionBounds.height > contentScreenRect.YMost()) { overscrollTranslation.y = contentScreenRect.YMost() - (userScroll.y + metrics.mCompositionBounds.height); } oldTransform.Translate(overscrollTranslation); gfxSize underZoomScale(1.0f, 1.0f); if (mContentRect.width * userZoom.scale < metrics.mCompositionBounds.width) { underZoomScale.width = (mContentRect.width * userZoom.scale) / metrics.mCompositionBounds.width; } if (mContentRect.height * userZoom.scale < metrics.mCompositionBounds.height) { underZoomScale.height = (mContentRect.height * userZoom.scale) / metrics.mCompositionBounds.height; } oldTransform.Scale(underZoomScale.width, underZoomScale.height, 1); // Make sure fixed position layers don't move away from their anchor points // when we're asynchronously panning or zooming AlignFixedLayersForAnchorPoint(aLayer, aLayer, oldTransform, fixedLayerMargins); }
bool AsyncCompositionManager::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFrame, Layer *aLayer, bool* aWantNextFrame) { bool appliedTransform = false; for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { appliedTransform |= ApplyAsyncContentTransformToTree(aCurrentFrame, child, aWantNextFrame); } ContainerLayer* container = aLayer->AsContainerLayer(); if (!container) { return appliedTransform; } if (AsyncPanZoomController* controller = container->GetAsyncPanZoomController()) { LayerComposite* layerComposite = aLayer->AsLayerComposite(); gfx3DMatrix oldTransform = aLayer->GetTransform(); ViewTransform treeTransform; ScreenPoint scrollOffset; *aWantNextFrame |= controller->SampleContentTransformForFrame(aCurrentFrame, &treeTransform, scrollOffset); const gfx3DMatrix& rootTransform = mLayerManager->GetRoot()->GetTransform(); const FrameMetrics& metrics = container->GetFrameMetrics(); // XXX We use rootTransform instead of metrics.mResolution here because on // Fennec the resolution is set on the root layer rather than the scrollable layer. // The SyncFrameMetrics call and the paintScale variable are used on Fennec only // so it doesn't affect any other platforms. See bug 732971. CSSToLayerScale paintScale = metrics.mDevPixelsPerCSSPixel / LayerToLayoutDeviceScale(rootTransform.GetXScale(), rootTransform.GetYScale()); CSSRect displayPort(metrics.mCriticalDisplayPort.IsEmpty() ? metrics.mDisplayPort : metrics.mCriticalDisplayPort); LayerMargin fixedLayerMargins(0, 0, 0, 0); ScreenPoint offset(0, 0); SyncFrameMetrics(scrollOffset, treeTransform.mScale.scale, metrics.mScrollableRect, mLayersUpdated, displayPort, paintScale, mIsFirstPaint, fixedLayerMargins, offset); mIsFirstPaint = false; mLayersUpdated = false; // Apply the render offset mLayerManager->GetCompositor()->SetScreenRenderOffset(offset); gfx3DMatrix transform(gfx3DMatrix(treeTransform) * aLayer->GetTransform()); // The transform already takes the resolution scale into account. Since we // will apply the resolution scale again when computing the effective // transform, we must apply the inverse resolution scale here. transform.Scale(1.0f/container->GetPreXScale(), 1.0f/container->GetPreYScale(), 1); transform.ScalePost(1.0f/aLayer->GetPostXScale(), 1.0f/aLayer->GetPostYScale(), 1); layerComposite->SetShadowTransform(transform); NS_ASSERTION(!layerComposite->GetShadowTransformSetByAnimation(), "overwriting animated transform!"); // Apply resolution scaling to the old transform - the layer tree as it is // doesn't have the necessary transform to display correctly. #ifdef MOZ_WIDGET_ANDROID // XXX We use rootTransform instead of the resolution on the individual layer's // FrameMetrics on Fennec because the resolution is set on the root layer rather // than the scrollable layer. See bug 732971. On non-Fennec we do the right thing. LayoutDeviceToLayerScale resolution(1.0 / rootTransform.GetXScale(), 1.0 / rootTransform.GetYScale()); #else LayoutDeviceToLayerScale resolution = metrics.mResolution; #endif oldTransform.Scale(resolution.scale, resolution.scale, 1); AlignFixedLayersForAnchorPoint(aLayer, aLayer, oldTransform, fixedLayerMargins); appliedTransform = true; } return appliedTransform; }
void CompositorParent::TransformShadowTree() { Layer* layer = GetPrimaryScrollableLayer(); ShadowLayer* shadow = layer->AsShadowLayer(); ContainerLayer* container = layer->AsContainerLayer(); const FrameMetrics* metrics = &container->GetFrameMetrics(); const gfx3DMatrix& rootTransform = mLayerManager->GetRoot()->GetTransform(); const gfx3DMatrix& currentTransform = layer->GetTransform(); float rootScaleX = rootTransform.GetXScale(); float rootScaleY = rootTransform.GetYScale(); if (mIsFirstPaint && metrics) { nsIntPoint scrollOffset = metrics->mViewportScrollOffset; mContentSize = metrics->mContentSize; SetFirstPaintViewport(scrollOffset.x, scrollOffset.y, 1/rootScaleX, mContentSize.width, mContentSize.height, metrics->mCSSContentSize.width, metrics->mCSSContentSize.height); mIsFirstPaint = false; } else if (metrics && (metrics->mContentSize != mContentSize)) { mContentSize = metrics->mContentSize; SetPageSize(1/rootScaleX, mContentSize.width, mContentSize.height, metrics->mCSSContentSize.width, metrics->mCSSContentSize.height); } // We synchronise the viewport information with Java after sending the above // notifications, so that Java can take these into account in its response. if (metrics) { // Calculate the absolute display port to send to Java nsIntRect displayPort = metrics->mDisplayPort; nsIntPoint scrollOffset = metrics->mViewportScrollOffset; displayPort.x += scrollOffset.x; displayPort.y += scrollOffset.y; SyncViewportInfo(displayPort, 1/rootScaleX, mLayersUpdated, mScrollOffset, mXScale, mYScale); mLayersUpdated = false; } // 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. if (metrics) { float tempScaleDiffX = rootScaleX * mXScale; float tempScaleDiffY = rootScaleY * mYScale; nsIntPoint metricsScrollOffset(0, 0); if (metrics->IsScrollable()) metricsScrollOffset = metrics->mViewportScrollOffset; nsIntPoint scrollCompensation( (mScrollOffset.x / tempScaleDiffX - metricsScrollOffset.x) * mXScale, (mScrollOffset.y / tempScaleDiffY - metricsScrollOffset.y) * mYScale); ViewTransform treeTransform(-scrollCompensation, mXScale, mYScale); shadow->SetShadowTransform(gfx3DMatrix(treeTransform) * currentTransform); } else { ViewTransform treeTransform(nsIntPoint(0,0), mXScale, mYScale); shadow->SetShadowTransform(gfx3DMatrix(treeTransform) * currentTransform); } }
void CompositorParent::TransformShadowTree() { Layer* layer = GetPrimaryScrollableLayer(); ShadowLayer* shadow = layer->AsShadowLayer(); ContainerLayer* container = layer->AsContainerLayer(); const FrameMetrics& metrics = container->GetFrameMetrics(); const gfx3DMatrix& rootTransform = mLayerManager->GetRoot()->GetTransform(); const gfx3DMatrix& currentTransform = layer->GetTransform(); float rootScaleX = rootTransform.GetXScale(); float rootScaleY = rootTransform.GetYScale(); if (mIsFirstPaint) { mContentRect = metrics.mContentRect; SetFirstPaintViewport(metrics.mViewportScrollOffset, 1/rootScaleX, mContentRect, metrics.mCSSContentRect); mIsFirstPaint = false; } else if (!metrics.mContentRect.IsEqualEdges(mContentRect)) { mContentRect = metrics.mContentRect; SetPageRect(1/rootScaleX, mContentRect, metrics.mCSSContentRect); } // We synchronise the viewport information with Java after sending the above // notifications, so that Java can take these into account in its response. // Calculate the absolute display port to send to Java nsIntRect displayPort = metrics.mDisplayPort; nsIntPoint scrollOffset = metrics.mViewportScrollOffset; displayPort.x += scrollOffset.x; displayPort.y += scrollOffset.y; SyncViewportInfo(displayPort, 1/rootScaleX, mLayersUpdated, mScrollOffset, mXScale, mYScale); mLayersUpdated = false; // 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 * mXScale; float tempScaleDiffY = rootScaleY * mYScale; nsIntPoint metricsScrollOffset(0, 0); if (metrics.IsScrollable()) metricsScrollOffset = metrics.mViewportScrollOffset; nsIntPoint scrollCompensation( (mScrollOffset.x / tempScaleDiffX - metricsScrollOffset.x) * mXScale, (mScrollOffset.y / tempScaleDiffY - metricsScrollOffset.y) * mYScale); ViewTransform treeTransform(-scrollCompensation, mXScale, mYScale); shadow->SetShadowTransform(gfx3DMatrix(treeTransform) * currentTransform); // Alter the scroll offset so that fixed position layers remain within // the page area. float offsetX = mScrollOffset.x / tempScaleDiffX; float offsetY = mScrollOffset.y / tempScaleDiffY; offsetX = NS_MAX((float)mContentRect.x, NS_MIN(offsetX, (float)(mContentRect.XMost() - mWidgetSize.width))); offsetY = NS_MAX((float)mContentRect.y, NS_MIN(offsetY, (float)(mContentRect.YMost() - mWidgetSize.height))); gfxPoint reverseViewTranslation(offsetX - metricsScrollOffset.x, offsetY - metricsScrollOffset.y); TranslateFixedLayers(layer, reverseViewTranslation); }
void RectTriangles::addRect(GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat tx0, GLfloat ty0, GLfloat tx1, GLfloat ty1, bool flip_y /* = false */) { if (vertexCoords.IsEmpty() && x0 == 0.0f && y0 == 0.0f && x1 == 1.0f && y1 == 1.0f) { mIsSimpleQuad = true; if (flip_y) { mTextureTransform = gfx3DMatrix::From2D(gfxMatrix(tx1 - tx0, 0, 0, ty0 - ty1, tx0, ty1)); } else { mTextureTransform = gfx3DMatrix::From2D(gfxMatrix(tx1 - tx0, 0, 0, ty1 - ty0, tx0, ty0)); } } else if (mIsSimpleQuad) { mIsSimpleQuad = false; mTextureTransform = gfx3DMatrix(); } vert_coord v; v.x = x0; v.y = y0; vertexCoords.AppendElement(v); v.x = x1; v.y = y0; vertexCoords.AppendElement(v); v.x = x0; v.y = y1; vertexCoords.AppendElement(v); v.x = x0; v.y = y1; vertexCoords.AppendElement(v); v.x = x1; v.y = y0; vertexCoords.AppendElement(v); v.x = x1; v.y = y1; vertexCoords.AppendElement(v); if (flip_y) { tex_coord t; t.u = tx0; t.v = ty1; texCoords.AppendElement(t); t.u = tx1; t.v = ty1; texCoords.AppendElement(t); t.u = tx0; t.v = ty0; texCoords.AppendElement(t); t.u = tx0; t.v = ty0; texCoords.AppendElement(t); t.u = tx1; t.v = ty1; texCoords.AppendElement(t); t.u = tx1; t.v = ty0; texCoords.AppendElement(t); } else { tex_coord t; t.u = tx0; t.v = ty0; texCoords.AppendElement(t); t.u = tx1; t.v = ty0; texCoords.AppendElement(t); t.u = tx0; t.v = ty1; texCoords.AppendElement(t); t.u = tx0; t.v = ty1; texCoords.AppendElement(t); t.u = tx1; t.v = ty0; texCoords.AppendElement(t); t.u = tx1; t.v = ty1; texCoords.AppendElement(t); } }
void ContainerLayerOGL::RenderLayer(int aPreviousFrameBuffer, const nsIntPoint& aOffset) { /** * Setup our temporary texture for rendering the contents of this container. */ GLuint containerSurface; GLuint frameBuffer; nsIntPoint childOffset(aOffset); nsIntRect visibleRect = GetEffectiveVisibleRegion().GetBounds(); nsIntRect cachedScissor = gl()->ScissorRect(); gl()->PushScissorRect(); mSupportsComponentAlphaChildren = false; float opacity = GetEffectiveOpacity(); const gfx3DMatrix& transform = GetEffectiveTransform(); bool needsFramebuffer = UseIntermediateSurface(); if (needsFramebuffer) { nsIntRect framebufferRect = visibleRect; // we're about to create a framebuffer backed by textures to use as an intermediate // surface. What to do if its size (as given by framebufferRect) would exceed the // maximum texture size supported by the GL? The present code chooses the compromise // of just clamping the framebuffer's size to the max supported size. // This gives us a lower resolution rendering of the intermediate surface (children layers). // See bug 827170 for a discussion. GLint maxTexSize; gl()->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &maxTexSize); framebufferRect.width = std::min(framebufferRect.width, maxTexSize); framebufferRect.height = std::min(framebufferRect.height, maxTexSize); LayerManagerOGL::InitMode mode = LayerManagerOGL::InitModeClear; if (GetEffectiveVisibleRegion().GetNumRects() == 1 && (GetContentFlags() & Layer::CONTENT_OPAQUE)) { // don't need a background, we're going to paint all opaque stuff mSupportsComponentAlphaChildren = true; mode = LayerManagerOGL::InitModeNone; } else { const gfx3DMatrix& transform3D = GetEffectiveTransform(); gfxMatrix transform; // If we have an opaque ancestor layer, then we can be sure that // all the pixels we draw into are either opaque already or will be // covered by something opaque. Otherwise copying up the background is // not safe. if (HasOpaqueAncestorLayer(this) && transform3D.Is2D(&transform) && !transform.HasNonIntegerTranslation()) { mode = gfxPlatform::ComponentAlphaEnabled() ? LayerManagerOGL::InitModeCopy : LayerManagerOGL::InitModeClear; framebufferRect.x += transform.x0; framebufferRect.y += transform.y0; mSupportsComponentAlphaChildren = gfxPlatform::ComponentAlphaEnabled(); } } gl()->PushViewportRect(); framebufferRect -= childOffset; if (!mOGLManager->CompositingDisabled()) { if (!mOGLManager->CreateFBOWithTexture(framebufferRect, mode, aPreviousFrameBuffer, &frameBuffer, &containerSurface)) { gl()->PopViewportRect(); gl()->PopScissorRect(); gl()->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, aPreviousFrameBuffer); return; } } childOffset.x = visibleRect.x; childOffset.y = visibleRect.y; } else { frameBuffer = aPreviousFrameBuffer; mSupportsComponentAlphaChildren = (GetContentFlags() & Layer::CONTENT_OPAQUE) || (GetParent() && GetParent()->SupportsComponentAlphaChildren()); } nsAutoTArray<Layer*, 12> children; SortChildrenBy3DZOrder(children); /** * Render this container's contents. */ for (uint32_t i = 0; i < children.Length(); i++) { LayerOGL* layerToRender = static_cast<LayerOGL*>(children.ElementAt(i)->ImplData()); if (layerToRender->GetLayer()->GetEffectiveVisibleRegion().IsEmpty()) { continue; } nsIntRect scissorRect = layerToRender->GetLayer()-> CalculateScissorRect(cachedScissor, &mOGLManager->GetWorldTransform()); if (scissorRect.IsEmpty()) { continue; } gl()->fScissor(scissorRect.x, scissorRect.y, scissorRect.width, scissorRect.height); layerToRender->RenderLayer(frameBuffer, childOffset); gl()->MakeCurrent(); } if (needsFramebuffer) { // Unbind the current framebuffer and rebind the previous one. #ifdef MOZ_DUMP_PAINTING if (gfxUtils::sDumpPainting) { nsRefPtr<gfxImageSurface> surf = gl()->GetTexImage(containerSurface, true, mOGLManager->GetFBOTextureFormat()); WriteSnapshotToDumpFile(this, surf); } #endif // Restore the viewport gl()->PopViewportRect(); nsIntRect viewport = gl()->ViewportRect(); mOGLManager->SetupPipeline(viewport.width, viewport.height, LayerManagerOGL::ApplyWorldTransform); gl()->PopScissorRect(); if (!mOGLManager->CompositingDisabled()) { gl()->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, aPreviousFrameBuffer); gl()->fDeleteFramebuffers(1, &frameBuffer); gl()->fActiveTexture(LOCAL_GL_TEXTURE0); gl()->fBindTexture(mOGLManager->FBOTextureTarget(), containerSurface); MaskType maskType = MaskNone; if (GetMaskLayer()) { if (!GetTransform().CanDraw2D()) { maskType = Mask3d; } else { maskType = Mask2d; } } ShaderProgramOGL *rgb = mOGLManager->GetFBOLayerProgram(maskType); rgb->Activate(); rgb->SetLayerQuadRect(visibleRect); rgb->SetLayerTransform(transform); rgb->SetTextureTransform(gfx3DMatrix()); rgb->SetLayerOpacity(opacity); rgb->SetRenderOffset(aOffset); rgb->SetTextureUnit(0); rgb->LoadMask(GetMaskLayer()); if (rgb->GetTexCoordMultiplierUniformLocation() != -1) { // 2DRect case, get the multiplier right for a sampler2DRect rgb->SetTexCoordMultiplier(visibleRect.width, visibleRect.height); } // Drawing is always flipped, but when copying between surfaces we want to avoid // this. Pass true for the flip parameter to introduce a second flip // that cancels the other one out. mOGLManager->BindAndDrawQuad(rgb, true); // Clean up resources. This also unbinds the texture. gl()->fDeleteTextures(1, &containerSurface); } } else { gl()->PopScissorRect(); } }