Bug 927349 part 10 - Record the time when animations are ready to start; r=jwatt
[gecko.git] / gfx / layers / basic / BasicLayerManager.cpp
bloba0ceceb1012119f58597663f3a5777c71eb0879a
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
51 class nsIWidget;
53 namespace mozilla {
54 namespace layers {
56 using namespace mozilla::gfx;
58 /**
59 * Clips to the smallest device-pixel-aligned rectangle containing aRect
60 * in user space.
61 * Returns true if the clip is "perfect", i.e. we actually clipped exactly to
62 * aRect.
64 static bool
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());
73 aContext->NewPath();
74 aContext->Rectangle(deviceRect);
75 aContext->Clip();
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);
100 result = aContext;
101 } else {
102 *aNeedsClipToVisibleRegion = false;
103 result = aContext;
104 if (aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) {
105 aContext->PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA);
106 } else {
107 aContext->PushGroup(gfxContentType::COLOR_ALPHA);
110 return result.forget();
113 static nsIntRect
114 ToInsideIntRect(const gfxRect& aRect)
116 gfxRect r = aRect;
117 r.RoundIn();
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 {
127 public:
128 PaintLayerContext(gfxContext* aTarget, Layer* aLayer,
129 LayerManager::DrawPaintedLayerCallback aCallback,
130 void* aCallbackData)
131 : mTarget(aTarget)
132 , mTargetMatrixSR(aTarget)
133 , mLayer(aLayer)
134 , mCallback(aCallback)
135 , mCallbackData(aCallbackData)
136 , mPushedOpaqueRect(false)
139 ~PaintLayerContext()
141 // Matrix is restored by mTargetMatrixSR
142 if (mPushedOpaqueRect)
144 ClearOpaqueRect();
148 // Gets the effective transform and returns true if it is a 2D
149 // transform.
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());
196 gfxContext* mTarget;
197 gfxContextMatrixAutoSaveRestore mTargetMatrixSR;
198 Layer* mLayer;
199 LayerManager::DrawPaintedLayerCallback mCallback;
200 void* mCallbackData;
201 Matrix mTransform;
202 bool mPushedOpaqueRect;
205 BasicLayerManager::BasicLayerManager(nsIWidget* aWidget)
206 : mPhase(PHASE_NONE)
207 , mWidget(aWidget)
208 , mDoubleBuffering(BufferMode::BUFFER_NONE)
209 , mType(BLM_WIDGET)
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)
219 : mPhase(PHASE_NONE)
220 , mWidget(nullptr)
221 , mDoubleBuffering(BufferMode::BUFFER_NONE)
222 , mType(aType)
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();
236 mRoot = nullptr;
238 MOZ_COUNT_DTOR(BasicLayerManager);
241 void
242 BasicLayerManager::SetDefaultTarget(gfxContext* aContext)
244 NS_ASSERTION(!InTransaction(),
245 "Must set default target outside transaction");
246 mDefaultTarget = aContext;
249 void
250 BasicLayerManager::SetDefaultTargetConfiguration(BufferMode aDoubleBuffering, ScreenRotation aRotation)
252 mDoubleBuffering = aDoubleBuffering;
255 void
256 BasicLayerManager::BeginTransaction()
258 mInTransaction = true;
259 mUsingDefaultTarget = true;
260 BeginTransactionWithTarget(mDefaultTarget);
263 void
264 BasicLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
266 mInTransaction = true;
268 #ifdef MOZ_LAYERS_HAVE_LOG
269 MOZ_LAYERS_LOG(("[----- BeginTransaction"));
270 Log();
271 #endif
273 NS_ASSERTION(!InTransaction(), "Nested transactions not allowed");
274 mPhase = PHASE_CONSTRUCTION;
275 mTarget = aTarget;
278 static void
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.
303 enum {
304 ALLOW_OPAQUE = 0x01,
306 static void
307 MarkLayersHidden(Layer* aLayer, const nsIntRect& aClipRect,
308 const nsIntRect& aDirtyRect,
309 nsIntRegion& aOpaqueRegion,
310 uint32_t aFlags)
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();
323 if (clipRect) {
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()) {
328 Matrix tr;
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);
333 } else {
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()) {
347 Matrix transform;
348 if (!aLayer->GetEffectiveTransform().CanDraw2D(&transform)) {
349 data->SetHidden(false);
350 return;
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
360 // content is opaque
361 if ((aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
362 (newFlags & ALLOW_OPAQUE)) {
363 nsIntRegionRectIterator it(region);
364 while (const nsIntRect* sr = it.Next()) {
365 r = *sr;
366 TransformIntRect(r, transform, ToInsideIntRect);
368 r.IntersectRect(r, newClipRect);
369 aOpaqueRegion.Or(aOpaqueRegion, r);
372 } else {
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()) {
378 allHidden = false;
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.
392 static void
393 ApplyDoubleBuffering(Layer* aLayer, const nsIntRect& aVisibleRect)
395 BasicImplData* data = ToData(aLayer);
396 if (data->IsHidden())
397 return;
399 nsIntRect newVisibleRect(aVisibleRect);
402 const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
403 if (clipRect) {
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()) {
408 Matrix tr;
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));
413 } else {
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.
426 if (!container) {
427 data->SetOperator(CompositionOp::OP_SOURCE);
428 data->SetDrawAtomically(true);
429 } else {
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();
435 } else {
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);
447 void
448 BasicLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
449 void* aCallbackData,
450 EndTransactionFlags aFlags)
452 mInTransaction = false;
454 EndTransactionInternal(aCallback, aCallbackData, aFlags);
457 void
458 BasicLayerManager::AbortTransaction()
460 NS_ASSERTION(InConstruction(), "Should be in construction phase");
461 mPhase = PHASE_NONE;
462 mUsingDefaultTarget = false;
463 mInTransaction = false;
466 bool
467 BasicLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback,
468 void* aCallbackData,
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)"));
476 Log();
477 #endif
479 NS_ASSERTION(InConstruction(), "Should be in construction phase");
480 mPhase = PHASE_DRAWING;
482 RenderTraceLayers(mRoot, "FF00");
484 mTransactionIncomplete = false;
486 if (mRoot) {
487 if (aFlags & END_NO_COMPOSITE) {
488 // Apply pending tree updates before recomputing effective
489 // properties.
490 mRoot->ApplyPendingUpdatesToSubtree();
493 // Need to do this before we call ApplyDoubleBuffering,
494 // which depends on correct effective transforms
495 if (mTarget) {
496 mSnapEffectiveTransforms =
497 !mTarget->GetDrawTarget()->GetUserData(&sDisablePixelSnapping);
498 } else {
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)) {
512 nsIntRect clipRect;
515 gfxContextMatrixAutoSaveRestore save(mTarget);
516 mTarget->SetMatrix(gfxMatrix());
517 clipRect = ToOutsideIntRect(mTarget->GetClipExtents());
520 if (IsRetained()) {
521 nsIntRegion region;
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);
531 const nsIntRect *r;
532 while ((r = iter.Next())) {
533 mTarget->GetDrawTarget()->ClearRect(Rect(r->x, r->y, r->width, r->height));
536 if (mWidget) {
537 FlashWidgetUpdateArea(mTarget);
539 RecordFrame();
540 PostPresent();
542 if (!mTransactionIncomplete) {
543 // Clear out target if we have a complete transaction.
544 mTarget = nullptr;
548 if (mRoot) {
549 mAnimationReadyTime = TimeStamp::Now();
552 #ifdef MOZ_LAYERS_HAVE_LOG
553 Log();
554 MOZ_LAYERS_LOG(("]----- EndTransaction"));
555 #endif
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;
575 void
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));
583 aContext->Paint();
587 bool
588 BasicLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags)
590 mInTransaction = false;
592 if (!mRoot) {
593 return false;
596 return EndTransactionInternal(nullptr, nullptr, aFlags);
599 void
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");
605 mRoot = aLayer;
608 static SkMatrix
609 BasicLayerManager_Matrix3DToSkia(const gfx3DMatrix& aMatrix)
611 SkMatrix transform;
612 transform.setAll(aMatrix._11,
613 aMatrix._21,
614 aMatrix._41,
615 aMatrix._12,
616 aMatrix._22,
617 aMatrix._42,
618 aMatrix._14,
619 aMatrix._24,
620 aMatrix._44);
622 return transform;
625 static void
626 SkiaTransform(const gfxImageSurface* aDest,
627 RefPtr<DataSourceSurface> aSrc,
628 const gfx3DMatrix& aTransform,
629 gfxPoint aDestOffset)
631 if (aTransform.IsSingular()) {
632 return;
635 IntSize destSize = ToIntSize(aDest->GetSize());
636 SkImageInfo destInfo = SkImageInfo::Make(destSize.width,
637 destSize.height,
638 kBGRA_8888_SkColorType,
639 kPremul_SkAlphaType);
640 SkBitmap destBitmap;
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,
647 srcSize.height,
648 kBGRA_8888_SkColorType,
649 kPremul_SkAlphaType);
650 SkBitmap src;
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));
658 SkPaint paint;
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
676 * a surface.
677 * @return Transformed surface
679 static already_AddRefed<gfxASurface>
680 Transform3D(RefPtr<SourceSurface> aSource,
681 gfxContext* aDest,
682 const gfxRect& aBounds,
683 const gfx3DMatrix& aTransform,
684 gfxRect& aDestRect)
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,
698 aDestRect.height),
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();
713 void
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();
721 if (!child) {
722 if (aPaintContext.mLayer->AsPaintedLayer()) {
723 data->PaintThebes(aGroupTarget, aPaintContext.mLayer->GetMaskLayer(),
724 aPaintContext.mCallback, aPaintContext.mCallbackData);
725 } else {
726 data->Paint(aGroupTarget->GetDrawTarget(),
727 aGroupTarget->GetDeviceOffset(),
728 aPaintContext.mLayer->GetMaskLayer());
730 } else {
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)
739 break;
744 void
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
755 // for the window.
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());
770 void
771 BasicLayerManager::PaintLayer(gfxContext* aTarget,
772 Layer* aLayer,
773 DrawPaintedLayerCallback aCallback,
774 void* aCallbackData)
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()) {
784 return;
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;
804 gfxMatrix transform;
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);
814 if (clipRect) {
815 aTarget->NewPath();
816 aTarget->SnappedRectangle(gfxRect(clipRect->x, clipRect->y, clipRect->width, clipRect->height));
817 aTarget->Clip();
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;
831 if (is2D) {
832 paintLayerContext.AnnotateOpaqueRect();
835 bool clipIsEmpty = !aTarget || aTarget->GetClipExtents().IsEmpty();
836 if (clipIsEmpty) {
837 PaintSelfOrChildren(paintLayerContext, aTarget);
838 return;
841 if (is2D) {
842 if (needsGroup) {
843 nsRefPtr<gfxContext> groupTarget = PushGroupForLayer(aTarget, aLayer, aLayer->GetEffectiveVisibleRegion(),
844 &needsClipToVisibleRegion);
845 PaintSelfOrChildren(paintLayerContext, groupTarget);
846 aTarget->PopGroupToSource();
847 FlushGroup(paintLayerContext, needsClipToVisibleRegion);
848 } else {
849 PaintSelfOrChildren(paintLayerContext, aTarget);
851 } else {
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) {
857 return;
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!");
869 gfxRect destRect;
870 #ifdef DEBUG
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,
875 1.0);
877 nsRefPtr<gfxContext> temp = new gfxContext(untransformedDT, Point(bounds.x, bounds.y));
878 temp->SetColor(color);
879 temp->Paint();
881 #endif
882 gfx3DMatrix effectiveTransform;
883 effectiveTransform = gfx::To3DMatrix(aLayer->GetEffectiveTransform());
884 nsRefPtr<gfxASurface> result =
885 Transform3D(untransformedDT->Snapshot(), aTarget, bounds,
886 effectiveTransform, destRect);
888 if (result) {
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.
893 aTarget->NewPath();
894 aTarget->SnappedRectangle(destRect);
895 aTarget->Clip();
896 FlushGroup(paintLayerContext, needsClipToVisibleRegion);
901 void
902 BasicLayerManager::ClearCachedResources(Layer* aSubtree)
904 MOZ_ASSERT(!aSubtree || aSubtree->Manager() == this);
905 if (aSubtree) {
906 ClearLayer(aSubtree);
907 } else if (mRoot) {
908 ClearLayer(mRoot);
911 void
912 BasicLayerManager::ClearLayer(Layer* aLayer)
914 ToData(aLayer)->ClearCachedResources();
915 for (Layer* child = aLayer->GetFirstChild(); child;
916 child = child->GetNextSibling()) {
917 ClearLayer(child);
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();