1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include <stdint.h> // for uint32_t
7 #include <stdlib.h> // for rand, RAND_MAX
8 #include <sys/types.h> // for int32_t
9 #include "BasicContainerLayer.h" // for BasicContainerLayer
10 #include "BasicLayersImpl.h" // for ToData, BasicReadbackLayer, etc
11 #include "GeckoProfiler.h" // for PROFILER_LABEL
12 #include "ImageContainer.h" // for ImageFactory
13 #include "Layers.h" // for Layer, ContainerLayer, etc
14 #include "ReadbackLayer.h" // for ReadbackLayer
15 #include "ReadbackProcessor.h" // for ReadbackProcessor
16 #include "RenderTrace.h" // for RenderTraceLayers, etc
17 #include "basic/BasicImplData.h" // for BasicImplData
18 #include "basic/BasicLayers.h" // for BasicLayerManager, etc
19 #include "gfx3DMatrix.h" // for gfx3DMatrix
20 #include "gfxASurface.h" // for gfxASurface, etc
21 #include "gfxColor.h" // for gfxRGBA
22 #include "gfxContext.h" // for gfxContext, etc
23 #include "gfxImageSurface.h" // for gfxImageSurface
24 #include "gfxMatrix.h" // for gfxMatrix
25 #include "gfxPlatform.h" // for gfxPlatform
26 #include "gfxPrefs.h" // for gfxPrefs
27 #include "gfxPoint.h" // for gfxIntSize, gfxPoint
28 #include "gfxRect.h" // for gfxRect
29 #include "gfxUtils.h" // for gfxUtils
30 #include "gfx2DGlue.h" // for thebes --> moz2d transition
31 #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
32 #include "mozilla/WidgetUtils.h" // for ScreenRotation
33 #include "mozilla/gfx/2D.h" // for DrawTarget
34 #include "mozilla/gfx/BasePoint.h" // for BasePoint
35 #include "mozilla/gfx/BaseRect.h" // for BaseRect
36 #include "mozilla/gfx/Matrix.h" // for Matrix
37 #include "mozilla/gfx/PathHelpers.h"
38 #include "mozilla/gfx/Rect.h" // for IntRect, Rect
39 #include "mozilla/layers/LayersTypes.h" // for BufferMode::BUFFER_NONE, etc
40 #include "mozilla/mozalloc.h" // for operator new
41 #include "nsAutoPtr.h" // for nsRefPtr
42 #include "nsCOMPtr.h" // for already_AddRefed
43 #include "nsDebug.h" // for NS_ASSERTION, etc
44 #include "nsISupportsImpl.h" // for gfxContext::Release, etc
45 #include "nsPoint.h" // for nsIntPoint
46 #include "nsRect.h" // for nsIntRect
47 #include "nsRegion.h" // for nsIntRegion, etc
48 #include "nsTArray.h" // for nsAutoTArray
49 #include "skia/SkCanvas.h" // for SkCanvas
50 #include "skia/SkBitmapDevice.h" // for SkBitmapDevice
56 using namespace mozilla::gfx
;
59 * Clips to the smallest device-pixel-aligned rectangle containing aRect
61 * Returns true if the clip is "perfect", i.e. we actually clipped exactly to
65 ClipToContain(gfxContext
* aContext
, const nsIntRect
& aRect
)
67 gfxRect
userRect(aRect
.x
, aRect
.y
, aRect
.width
, aRect
.height
);
68 gfxRect deviceRect
= aContext
->UserToDevice(userRect
);
69 deviceRect
.RoundOut();
71 gfxMatrix currentMatrix
= aContext
->CurrentMatrix();
72 aContext
->SetMatrix(gfxMatrix());
74 aContext
->Rectangle(deviceRect
);
76 aContext
->SetMatrix(currentMatrix
);
78 return aContext
->DeviceToUser(deviceRect
).IsEqualInterior(userRect
);
81 already_AddRefed
<gfxContext
>
82 BasicLayerManager::PushGroupForLayer(gfxContext
* aContext
, Layer
* aLayer
,
83 const nsIntRegion
& aRegion
,
84 bool* aNeedsClipToVisibleRegion
)
86 // If we need to call PushGroup, we should clip to the smallest possible
87 // area first to minimize the size of the temporary surface.
88 bool didCompleteClip
= ClipToContain(aContext
, aRegion
.GetBounds());
90 nsRefPtr
<gfxContext
> result
;
91 if (aLayer
->CanUseOpaqueSurface() &&
92 ((didCompleteClip
&& aRegion
.GetNumRects() == 1) ||
93 !aContext
->CurrentMatrix().HasNonIntegerTranslation())) {
94 // If the layer is opaque in its visible region we can push a gfxContentType::COLOR
95 // group. We need to make sure that only pixels inside the layer's visible
96 // region are copied back to the destination. Remember if we've already
97 // clipped precisely to the visible region.
98 *aNeedsClipToVisibleRegion
= !didCompleteClip
|| aRegion
.GetNumRects() > 1;
99 aContext
->PushGroup(gfxContentType::COLOR
);
102 *aNeedsClipToVisibleRegion
= false;
104 if (aLayer
->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA
) {
105 aContext
->PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA
);
107 aContext
->PushGroup(gfxContentType::COLOR_ALPHA
);
110 return result
.forget();
114 ToInsideIntRect(const gfxRect
& aRect
)
118 return nsIntRect(r
.X(), r
.Y(), r
.Width(), r
.Height());
121 // A context helper for BasicLayerManager::PaintLayer() that holds all the
122 // painting context together in a data structure so it can be easily passed
123 // around. It also uses ensures that the Transform and Opaque rect are restored
124 // to their former state on destruction.
126 class PaintLayerContext
{
128 PaintLayerContext(gfxContext
* aTarget
, Layer
* aLayer
,
129 LayerManager::DrawPaintedLayerCallback aCallback
,
132 , mTargetMatrixSR(aTarget
)
134 , mCallback(aCallback
)
135 , mCallbackData(aCallbackData
)
136 , mPushedOpaqueRect(false)
141 // Matrix is restored by mTargetMatrixSR
142 if (mPushedOpaqueRect
)
148 // Gets the effective transform and returns true if it is a 2D
150 bool Setup2DTransform()
152 // Will return an identity matrix for 3d transforms.
153 return mLayer
->GetEffectiveTransformForBuffer().CanDraw2D(&mTransform
);
156 // Applies the effective transform if it's 2D. If it's a 3D transform then
157 // it applies an identity.
158 void Apply2DTransform()
160 mTarget
->SetMatrix(ThebesMatrix(mTransform
));
163 // Set the opaque rect to match the bounds of the visible region.
164 void AnnotateOpaqueRect()
166 const nsIntRegion
& visibleRegion
= mLayer
->GetEffectiveVisibleRegion();
167 const nsIntRect
& bounds
= visibleRegion
.GetBounds();
169 DrawTarget
*dt
= mTarget
->GetDrawTarget();
170 const IntRect
& targetOpaqueRect
= dt
->GetOpaqueRect();
172 // Try to annotate currentSurface with a region of pixels that have been
173 // (or will be) painted opaque, if no such region is currently set.
174 if (targetOpaqueRect
.IsEmpty() && visibleRegion
.GetNumRects() == 1 &&
175 (mLayer
->GetContentFlags() & Layer::CONTENT_OPAQUE
) &&
176 !mTransform
.HasNonAxisAlignedTransform()) {
178 gfx::Rect opaqueRect
= dt
->GetTransform().TransformBounds(
179 gfx::Rect(bounds
.x
, bounds
.y
, bounds
.width
, bounds
.height
));
180 opaqueRect
.RoundIn();
181 IntRect intOpaqueRect
;
182 if (opaqueRect
.ToIntRect(&intOpaqueRect
)) {
183 mTarget
->GetDrawTarget()->SetOpaqueRect(intOpaqueRect
);
184 mPushedOpaqueRect
= true;
189 // Clear the Opaque rect. Although this doesn't really restore it to it's
190 // previous state it will happen on the exit path of the PaintLayer() so when
191 // painting is complete the opaque rect qill be clear.
192 void ClearOpaqueRect() {
193 mTarget
->GetDrawTarget()->SetOpaqueRect(IntRect());
197 gfxContextMatrixAutoSaveRestore mTargetMatrixSR
;
199 LayerManager::DrawPaintedLayerCallback mCallback
;
202 bool mPushedOpaqueRect
;
205 BasicLayerManager::BasicLayerManager(nsIWidget
* aWidget
)
208 , mDoubleBuffering(BufferMode::BUFFER_NONE
)
210 , mUsingDefaultTarget(false)
211 , mTransactionIncomplete(false)
212 , mCompositorMightResample(false)
214 MOZ_COUNT_CTOR(BasicLayerManager
);
215 NS_ASSERTION(aWidget
, "Must provide a widget");
218 BasicLayerManager::BasicLayerManager(BasicLayerManagerType aType
)
221 , mDoubleBuffering(BufferMode::BUFFER_NONE
)
223 , mUsingDefaultTarget(false)
224 , mTransactionIncomplete(false)
226 MOZ_COUNT_CTOR(BasicLayerManager
);
227 MOZ_ASSERT(mType
!= BLM_WIDGET
);
230 BasicLayerManager::~BasicLayerManager()
232 NS_ASSERTION(!InTransaction(), "Died during transaction?");
234 ClearCachedResources();
238 MOZ_COUNT_DTOR(BasicLayerManager
);
242 BasicLayerManager::SetDefaultTarget(gfxContext
* aContext
)
244 NS_ASSERTION(!InTransaction(),
245 "Must set default target outside transaction");
246 mDefaultTarget
= aContext
;
250 BasicLayerManager::SetDefaultTargetConfiguration(BufferMode aDoubleBuffering
, ScreenRotation aRotation
)
252 mDoubleBuffering
= aDoubleBuffering
;
256 BasicLayerManager::BeginTransaction()
258 mInTransaction
= true;
259 mUsingDefaultTarget
= true;
260 BeginTransactionWithTarget(mDefaultTarget
);
264 BasicLayerManager::BeginTransactionWithTarget(gfxContext
* aTarget
)
266 mInTransaction
= true;
268 #ifdef MOZ_LAYERS_HAVE_LOG
269 MOZ_LAYERS_LOG(("[----- BeginTransaction"));
273 NS_ASSERTION(!InTransaction(), "Nested transactions not allowed");
274 mPhase
= PHASE_CONSTRUCTION
;
279 TransformIntRect(nsIntRect
& aRect
, const Matrix
& aMatrix
,
280 nsIntRect (*aRoundMethod
)(const gfxRect
&))
282 Rect gr
= Rect(aRect
.x
, aRect
.y
, aRect
.width
, aRect
.height
);
283 gr
= aMatrix
.TransformBounds(gr
);
284 aRect
= (*aRoundMethod
)(ThebesRect(gr
));
288 * This function assumes that GetEffectiveTransform transforms
289 * all layers to the same coordinate system (the "root coordinate system").
290 * It can't be used as is by accelerated layers because of intermediate surfaces.
291 * This must set the hidden flag to true or false on *all* layers in the subtree.
292 * It also sets the operator for all layers to "OVER", and call
293 * SetDrawAtomically(false).
294 * It clears mClipToVisibleRegion on all layers.
295 * @param aClipRect the cliprect, in the root coordinate system. We assume
296 * that any layer drawing is clipped to this rect. It is therefore not
297 * allowed to add to the opaque region outside that rect.
298 * @param aDirtyRect the dirty rect that will be painted, in the root
299 * coordinate system. Layers outside this rect should be hidden.
300 * @param aOpaqueRegion the opaque region covering aLayer, in the
301 * root coordinate system.
307 MarkLayersHidden(Layer
* aLayer
, const nsIntRect
& aClipRect
,
308 const nsIntRect
& aDirtyRect
,
309 nsIntRegion
& aOpaqueRegion
,
312 nsIntRect
newClipRect(aClipRect
);
313 uint32_t newFlags
= aFlags
;
315 // Allow aLayer or aLayer's descendants to cover underlying layers
316 // only if it's opaque.
317 if (aLayer
->GetOpacity() != 1.0f
) {
318 newFlags
&= ~ALLOW_OPAQUE
;
322 const nsIntRect
* clipRect
= aLayer
->GetEffectiveClipRect();
324 nsIntRect cr
= *clipRect
;
325 // clipRect is in the container's coordinate system. Get it into the
326 // global coordinate system.
327 if (aLayer
->GetParent()) {
329 if (aLayer
->GetParent()->GetEffectiveTransform().CanDraw2D(&tr
)) {
330 // Clip rect is applied after aLayer's transform, i.e., in the coordinate
331 // system of aLayer's parent.
332 TransformIntRect(cr
, tr
, ToInsideIntRect
);
334 cr
.SetRect(0, 0, 0, 0);
337 newClipRect
.IntersectRect(newClipRect
, cr
);
341 BasicImplData
* data
= ToData(aLayer
);
342 data
->SetOperator(CompositionOp::OP_OVER
);
343 data
->SetClipToVisibleRegion(false);
344 data
->SetDrawAtomically(false);
346 if (!aLayer
->AsContainerLayer()) {
348 if (!aLayer
->GetEffectiveTransform().CanDraw2D(&transform
)) {
349 data
->SetHidden(false);
353 nsIntRegion region
= aLayer
->GetEffectiveVisibleRegion();
354 nsIntRect r
= region
.GetBounds();
355 TransformIntRect(r
, transform
, ToOutsideIntRect
);
356 r
.IntersectRect(r
, aDirtyRect
);
357 data
->SetHidden(aOpaqueRegion
.Contains(r
));
359 // Allow aLayer to cover underlying layers only if aLayer's
361 if ((aLayer
->GetContentFlags() & Layer::CONTENT_OPAQUE
) &&
362 (newFlags
& ALLOW_OPAQUE
)) {
363 nsIntRegionRectIterator
it(region
);
364 while (const nsIntRect
* sr
= it
.Next()) {
366 TransformIntRect(r
, transform
, ToInsideIntRect
);
368 r
.IntersectRect(r
, newClipRect
);
369 aOpaqueRegion
.Or(aOpaqueRegion
, r
);
373 Layer
* child
= aLayer
->GetLastChild();
374 bool allHidden
= true;
375 for (; child
; child
= child
->GetPrevSibling()) {
376 MarkLayersHidden(child
, newClipRect
, aDirtyRect
, aOpaqueRegion
, newFlags
);
377 if (!ToData(child
)->IsHidden()) {
381 data
->SetHidden(allHidden
);
386 * This function assumes that GetEffectiveTransform transforms
387 * all layers to the same coordinate system (the "root coordinate system").
388 * MarkLayersHidden must be called before calling this.
389 * @param aVisibleRect the rectangle of aLayer that is visible (i.e. not
390 * clipped and in the dirty rect), in the root coordinate system.
393 ApplyDoubleBuffering(Layer
* aLayer
, const nsIntRect
& aVisibleRect
)
395 BasicImplData
* data
= ToData(aLayer
);
396 if (data
->IsHidden())
399 nsIntRect
newVisibleRect(aVisibleRect
);
402 const nsIntRect
* clipRect
= aLayer
->GetEffectiveClipRect();
404 nsIntRect cr
= *clipRect
;
405 // clipRect is in the container's coordinate system. Get it into the
406 // global coordinate system.
407 if (aLayer
->GetParent()) {
409 if (aLayer
->GetParent()->GetEffectiveTransform().CanDraw2D(&tr
)) {
410 NS_ASSERTION(!ThebesMatrix(tr
).HasNonIntegerTranslation(),
411 "Parent can only have an integer translation");
412 cr
+= nsIntPoint(int32_t(tr
._31
), int32_t(tr
._32
));
414 NS_ERROR("Parent can only have an integer translation");
417 newVisibleRect
.IntersectRect(newVisibleRect
, cr
);
421 BasicContainerLayer
* container
=
422 static_cast<BasicContainerLayer
*>(aLayer
->AsContainerLayer());
423 // Layers that act as their own backbuffers should be drawn to the destination
424 // using OPERATOR_SOURCE to ensure that alpha values in a transparent window
425 // are cleared. This can also be faster than OPERATOR_OVER.
427 data
->SetOperator(CompositionOp::OP_SOURCE
);
428 data
->SetDrawAtomically(true);
430 if (container
->UseIntermediateSurface() ||
431 !container
->ChildrenPartitionVisibleRegion(newVisibleRect
)) {
432 // We need to double-buffer this container.
433 data
->SetOperator(CompositionOp::OP_SOURCE
);
434 container
->ForceIntermediateSurface();
436 // Tell the children to clip to their visible regions so our assumption
437 // that they don't paint outside their visible regions is valid!
438 for (Layer
* child
= aLayer
->GetFirstChild(); child
;
439 child
= child
->GetNextSibling()) {
440 ToData(child
)->SetClipToVisibleRegion(true);
441 ApplyDoubleBuffering(child
, newVisibleRect
);
448 BasicLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback
,
450 EndTransactionFlags aFlags
)
452 mInTransaction
= false;
454 EndTransactionInternal(aCallback
, aCallbackData
, aFlags
);
458 BasicLayerManager::AbortTransaction()
460 NS_ASSERTION(InConstruction(), "Should be in construction phase");
462 mUsingDefaultTarget
= false;
463 mInTransaction
= false;
467 BasicLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback
,
469 EndTransactionFlags aFlags
)
471 PROFILER_LABEL("BasicLayerManager", "EndTransactionInternal",
472 js::ProfileEntry::Category::GRAPHICS
);
474 #ifdef MOZ_LAYERS_HAVE_LOG
475 MOZ_LAYERS_LOG((" ----- (beginning paint)"));
479 NS_ASSERTION(InConstruction(), "Should be in construction phase");
480 mPhase
= PHASE_DRAWING
;
482 RenderTraceLayers(mRoot
, "FF00");
484 mTransactionIncomplete
= false;
487 if (aFlags
& END_NO_COMPOSITE
) {
488 // Apply pending tree updates before recomputing effective
490 mRoot
->ApplyPendingUpdatesToSubtree();
493 // Need to do this before we call ApplyDoubleBuffering,
494 // which depends on correct effective transforms
496 mSnapEffectiveTransforms
=
497 !mTarget
->GetDrawTarget()->GetUserData(&sDisablePixelSnapping
);
499 mSnapEffectiveTransforms
= true;
501 mRoot
->ComputeEffectiveTransforms(mTarget
? Matrix4x4::From2D(ToMatrix(mTarget
->CurrentMatrix())) : Matrix4x4());
503 ToData(mRoot
)->Validate(aCallback
, aCallbackData
, nullptr);
504 if (mRoot
->GetMaskLayer()) {
505 ToData(mRoot
->GetMaskLayer())->Validate(aCallback
, aCallbackData
, nullptr);
509 if (mTarget
&& mRoot
&&
510 !(aFlags
& END_NO_IMMEDIATE_REDRAW
) &&
511 !(aFlags
& END_NO_COMPOSITE
)) {
515 gfxContextMatrixAutoSaveRestore
save(mTarget
);
516 mTarget
->SetMatrix(gfxMatrix());
517 clipRect
= ToOutsideIntRect(mTarget
->GetClipExtents());
522 MarkLayersHidden(mRoot
, clipRect
, clipRect
, region
, ALLOW_OPAQUE
);
523 if (mUsingDefaultTarget
&& mDoubleBuffering
!= BufferMode::BUFFER_NONE
) {
524 ApplyDoubleBuffering(mRoot
, clipRect
);
528 PaintLayer(mTarget
, mRoot
, aCallback
, aCallbackData
);
529 if (!mRegionToClear
.IsEmpty()) {
530 nsIntRegionRectIterator
iter(mRegionToClear
);
532 while ((r
= iter
.Next())) {
533 mTarget
->GetDrawTarget()->ClearRect(Rect(r
->x
, r
->y
, r
->width
, r
->height
));
537 FlashWidgetUpdateArea(mTarget
);
542 if (!mTransactionIncomplete
) {
543 // Clear out target if we have a complete transaction.
549 mAnimationReadyTime
= TimeStamp::Now();
552 #ifdef MOZ_LAYERS_HAVE_LOG
554 MOZ_LAYERS_LOG(("]----- EndTransaction"));
557 // Go back to the construction phase if the transaction isn't complete.
558 // Layout will update the layer tree and call EndTransaction().
559 mPhase
= mTransactionIncomplete
? PHASE_CONSTRUCTION
: PHASE_NONE
;
561 if (!mTransactionIncomplete
) {
562 // This is still valid if the transaction was incomplete.
563 mUsingDefaultTarget
= false;
566 NS_ASSERTION(!aCallback
|| !mTransactionIncomplete
,
567 "If callback is not null, transaction must be complete");
569 // XXX - We should probably assert here that for an incomplete transaction
570 // out target is the default target.
572 return !mTransactionIncomplete
;
576 BasicLayerManager::FlashWidgetUpdateArea(gfxContext
*aContext
)
578 if (gfxPrefs::WidgetUpdateFlashing()) {
579 float r
= float(rand()) / RAND_MAX
;
580 float g
= float(rand()) / RAND_MAX
;
581 float b
= float(rand()) / RAND_MAX
;
582 aContext
->SetColor(gfxRGBA(r
, g
, b
, 0.2));
588 BasicLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags
)
590 mInTransaction
= false;
596 return EndTransactionInternal(nullptr, nullptr, aFlags
);
600 BasicLayerManager::SetRoot(Layer
* aLayer
)
602 NS_ASSERTION(aLayer
, "Root can't be null");
603 NS_ASSERTION(aLayer
->Manager() == this, "Wrong manager");
604 NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
609 BasicLayerManager_Matrix3DToSkia(const gfx3DMatrix
& aMatrix
)
612 transform
.setAll(aMatrix
._11
,
626 SkiaTransform(const gfxImageSurface
* aDest
,
627 RefPtr
<DataSourceSurface
> aSrc
,
628 const gfx3DMatrix
& aTransform
,
629 gfxPoint aDestOffset
)
631 if (aTransform
.IsSingular()) {
635 IntSize destSize
= ToIntSize(aDest
->GetSize());
636 SkImageInfo destInfo
= SkImageInfo::Make(destSize
.width
,
638 kBGRA_8888_SkColorType
,
639 kPremul_SkAlphaType
);
641 destBitmap
.setInfo(destInfo
, aDest
->Stride());
642 destBitmap
.setPixels((uint32_t*)aDest
->Data());
643 SkCanvas
destCanvas(destBitmap
);
645 IntSize srcSize
= aSrc
->GetSize();
646 SkImageInfo srcInfo
= SkImageInfo::Make(srcSize
.width
,
648 kBGRA_8888_SkColorType
,
649 kPremul_SkAlphaType
);
651 src
.setInfo(srcInfo
, aSrc
->Stride());
652 src
.setPixels((uint32_t*)aSrc
->GetData());
654 gfx3DMatrix transform
= aTransform
;
655 transform
.TranslatePost(Point3D(-aDestOffset
.x
, -aDestOffset
.y
, 0));
656 destCanvas
.setMatrix(BasicLayerManager_Matrix3DToSkia(transform
));
659 paint
.setXfermodeMode(SkXfermode::kSrc_Mode
);
660 paint
.setAntiAlias(true);
661 paint
.setFilterLevel(SkPaint::kLow_FilterLevel
);
662 SkRect destRect
= SkRect::MakeXYWH(0, 0, srcSize
.width
, srcSize
.height
);
663 destCanvas
.drawBitmapRectToRect(src
, nullptr, destRect
, &paint
);
667 * Transform a surface using a gfx3DMatrix and blit to the destination if
668 * it is efficient to do so.
670 * @param aSource Source surface.
671 * @param aDest Desintation context.
672 * @param aBounds Area represented by aSource.
673 * @param aTransform Transformation matrix.
674 * @param aDestRect Output: rectangle in which to draw returned surface on aDest
675 * (same size as aDest). Only filled in if this returns
677 * @return Transformed surface
679 static already_AddRefed
<gfxASurface
>
680 Transform3D(RefPtr
<SourceSurface
> aSource
,
682 const gfxRect
& aBounds
,
683 const gfx3DMatrix
& aTransform
,
686 // Find the transformed rectangle of our layer.
687 gfxRect offsetRect
= aTransform
.TransformBounds(aBounds
);
689 // Intersect the transformed layer with the destination rectangle.
690 // This is in device space since we have an identity transform set on aTarget.
691 aDestRect
= aDest
->GetClipExtents();
692 aDestRect
.IntersectRect(aDestRect
, offsetRect
);
693 aDestRect
.RoundOut();
695 // Create a surface the size of the transformed object.
696 nsRefPtr
<gfxASurface
> dest
= aDest
->CurrentSurface();
697 nsRefPtr
<gfxImageSurface
> destImage
= new gfxImageSurface(gfxIntSize(aDestRect
.width
,
699 gfxImageFormat::ARGB32
);
700 gfxPoint offset
= aDestRect
.TopLeft();
702 // Include a translation to the correct origin.
703 gfx3DMatrix translation
= gfx3DMatrix::Translation(aBounds
.x
, aBounds
.y
, 0);
705 // Transform the content and offset it such that the content begins at the origin.
706 SkiaTransform(destImage
, aSource
->GetDataSurface(), translation
* aTransform
, offset
);
708 // If we haven't actually drawn to aDest then return our temporary image so
709 // that the caller can do this.
710 return destImage
.forget();
714 BasicLayerManager::PaintSelfOrChildren(PaintLayerContext
& aPaintContext
,
715 gfxContext
* aGroupTarget
)
717 BasicImplData
* data
= ToData(aPaintContext
.mLayer
);
719 /* Only paint ourself, or our children - This optimization relies on this! */
720 Layer
* child
= aPaintContext
.mLayer
->GetFirstChild();
722 if (aPaintContext
.mLayer
->AsPaintedLayer()) {
723 data
->PaintThebes(aGroupTarget
, aPaintContext
.mLayer
->GetMaskLayer(),
724 aPaintContext
.mCallback
, aPaintContext
.mCallbackData
);
726 data
->Paint(aGroupTarget
->GetDrawTarget(),
727 aGroupTarget
->GetDeviceOffset(),
728 aPaintContext
.mLayer
->GetMaskLayer());
731 ContainerLayer
* container
=
732 static_cast<ContainerLayer
*>(aPaintContext
.mLayer
);
733 nsAutoTArray
<Layer
*, 12> children
;
734 container
->SortChildrenBy3DZOrder(children
);
735 for (uint32_t i
= 0; i
< children
.Length(); i
++) {
736 PaintLayer(aGroupTarget
, children
.ElementAt(i
), aPaintContext
.mCallback
,
737 aPaintContext
.mCallbackData
);
738 if (mTransactionIncomplete
)
745 BasicLayerManager::FlushGroup(PaintLayerContext
& aPaintContext
, bool aNeedsClipToVisibleRegion
)
747 // If we're doing our own double-buffering, we need to avoid drawing
748 // the results of an incomplete transaction to the destination surface ---
749 // that could cause flicker. Double-buffering is implemented using a
750 // temporary surface for one or more container layers, so we need to stop
751 // those temporary surfaces from being composited to aTarget.
752 // ApplyDoubleBuffering guarantees that this container layer can't
753 // intersect any other leaf layers, so if the transaction is not yet marked
754 // incomplete, the contents of this container layer are the final contents
756 if (!mTransactionIncomplete
) {
757 if (aNeedsClipToVisibleRegion
) {
758 gfxUtils::ClipToRegion(aPaintContext
.mTarget
,
759 aPaintContext
.mLayer
->GetEffectiveVisibleRegion());
762 CompositionOp op
= GetEffectiveOperator(aPaintContext
.mLayer
);
763 AutoSetOperator
setOperator(aPaintContext
.mTarget
, ThebesOp(op
));
765 PaintWithMask(aPaintContext
.mTarget
, aPaintContext
.mLayer
->GetEffectiveOpacity(),
766 aPaintContext
.mLayer
->GetMaskLayer());
771 BasicLayerManager::PaintLayer(gfxContext
* aTarget
,
773 DrawPaintedLayerCallback aCallback
,
776 PROFILER_LABEL("BasicLayerManager", "PaintLayer",
777 js::ProfileEntry::Category::GRAPHICS
);
779 PaintLayerContext
paintLayerContext(aTarget
, aLayer
, aCallback
, aCallbackData
);
781 // Don't attempt to paint layers with a singular transform, cairo will
782 // just throw an error.
783 if (aLayer
->GetEffectiveTransform().IsSingular()) {
787 RenderTraceScope
trace("BasicLayerManager::PaintLayer", "707070");
789 const nsIntRect
* clipRect
= aLayer
->GetEffectiveClipRect();
790 BasicContainerLayer
* container
=
791 static_cast<BasicContainerLayer
*>(aLayer
->AsContainerLayer());
792 bool needsGroup
= container
&& container
->UseIntermediateSurface();
793 BasicImplData
* data
= ToData(aLayer
);
794 bool needsClipToVisibleRegion
=
795 data
->GetClipToVisibleRegion() && !aLayer
->AsPaintedLayer();
796 NS_ASSERTION(needsGroup
|| !container
||
797 container
->GetOperator() == CompositionOp::OP_OVER
,
798 "non-OVER operator should have forced UseIntermediateSurface");
799 NS_ASSERTION(!container
|| !aLayer
->GetMaskLayer() ||
800 container
->UseIntermediateSurface(),
801 "ContainerLayer with mask layer should force UseIntermediateSurface");
803 gfxContextAutoSaveRestore contextSR
;
805 // Will return an identity matrix for 3d transforms, and is handled separately below.
806 bool is2D
= paintLayerContext
.Setup2DTransform();
807 NS_ABORT_IF_FALSE(is2D
|| needsGroup
|| !container
, "Must PushGroup for 3d transforms!");
809 bool needsSaveRestore
=
810 needsGroup
|| clipRect
|| needsClipToVisibleRegion
|| !is2D
;
811 if (needsSaveRestore
) {
812 contextSR
.SetContext(aTarget
);
816 aTarget
->SnappedRectangle(gfxRect(clipRect
->x
, clipRect
->y
, clipRect
->width
, clipRect
->height
));
821 paintLayerContext
.Apply2DTransform();
823 const nsIntRegion
& visibleRegion
= aLayer
->GetEffectiveVisibleRegion();
824 // If needsGroup is true, we'll clip to the visible region after we've popped the group
825 if (needsClipToVisibleRegion
&& !needsGroup
) {
826 gfxUtils::ClipToRegion(aTarget
, visibleRegion
);
827 // Don't need to clip to visible region again
828 needsClipToVisibleRegion
= false;
832 paintLayerContext
.AnnotateOpaqueRect();
835 bool clipIsEmpty
= !aTarget
|| aTarget
->GetClipExtents().IsEmpty();
837 PaintSelfOrChildren(paintLayerContext
, aTarget
);
843 nsRefPtr
<gfxContext
> groupTarget
= PushGroupForLayer(aTarget
, aLayer
, aLayer
->GetEffectiveVisibleRegion(),
844 &needsClipToVisibleRegion
);
845 PaintSelfOrChildren(paintLayerContext
, groupTarget
);
846 aTarget
->PopGroupToSource();
847 FlushGroup(paintLayerContext
, needsClipToVisibleRegion
);
849 PaintSelfOrChildren(paintLayerContext
, aTarget
);
852 const nsIntRect
& bounds
= visibleRegion
.GetBounds();
853 RefPtr
<DrawTarget
> untransformedDT
=
854 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(bounds
.width
, bounds
.height
),
855 SurfaceFormat::B8G8R8A8
);
856 if (!untransformedDT
) {
860 nsRefPtr
<gfxContext
> groupTarget
= new gfxContext(untransformedDT
,
861 Point(bounds
.x
, bounds
.y
));
863 PaintSelfOrChildren(paintLayerContext
, groupTarget
);
865 // Temporary fast fix for bug 725886
866 // Revert these changes when 725886 is ready
867 NS_ABORT_IF_FALSE(untransformedDT
,
868 "We should always allocate an untransformed surface with 3d transforms!");
871 if (aLayer
->GetDebugColorIndex() != 0) {
872 gfxRGBA
color((aLayer
->GetDebugColorIndex() & 1) ? 1.0 : 0.0,
873 (aLayer
->GetDebugColorIndex() & 2) ? 1.0 : 0.0,
874 (aLayer
->GetDebugColorIndex() & 4) ? 1.0 : 0.0,
877 nsRefPtr
<gfxContext
> temp
= new gfxContext(untransformedDT
, Point(bounds
.x
, bounds
.y
));
878 temp
->SetColor(color
);
882 gfx3DMatrix effectiveTransform
;
883 effectiveTransform
= gfx::To3DMatrix(aLayer
->GetEffectiveTransform());
884 nsRefPtr
<gfxASurface
> result
=
885 Transform3D(untransformedDT
->Snapshot(), aTarget
, bounds
,
886 effectiveTransform
, destRect
);
889 aTarget
->SetSource(result
, destRect
.TopLeft());
890 // Azure doesn't support EXTEND_NONE, so to avoid extending the edges
891 // of the source surface out to the current clip region, clip to
892 // the rectangle of the result surface now.
894 aTarget
->SnappedRectangle(destRect
);
896 FlushGroup(paintLayerContext
, needsClipToVisibleRegion
);
902 BasicLayerManager::ClearCachedResources(Layer
* aSubtree
)
904 MOZ_ASSERT(!aSubtree
|| aSubtree
->Manager() == this);
906 ClearLayer(aSubtree
);
912 BasicLayerManager::ClearLayer(Layer
* aLayer
)
914 ToData(aLayer
)->ClearCachedResources();
915 for (Layer
* child
= aLayer
->GetFirstChild(); child
;
916 child
= child
->GetNextSibling()) {
921 already_AddRefed
<ReadbackLayer
>
922 BasicLayerManager::CreateReadbackLayer()
924 NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
925 nsRefPtr
<ReadbackLayer
> layer
= new BasicReadbackLayer(this);
926 return layer
.forget();