1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef GFX_LayerManagerCompositeUtils_H
8 #define GFX_LayerManagerCompositeUtils_H
9 
10 #include <cstddef>                 // for size_t
11 #include "Layers.h"                // for Layer
12 #include "Units.h"                 // for LayerIntRegion
13 #include "mozilla/RefPtr.h"        // for RefPtr
14 #include "mozilla/gfx/BaseRect.h"  // for operator-
15 #include "mozilla/gfx/Matrix.h"    // for Matrix4x4
16 #include "mozilla/gfx/Rect.h"  // for IntRect, Rect, RoundedOut, IntRectTyped
17 #include "mozilla/layers/Compositor.h"  // for Compositor, INIT_MODE_CLEAR
18 #include "mozilla/layers/Effects.h"     // for EffectChain, EffectRenderTarget
19 #include "mozilla/layers/LayerManagerComposite.h"  // for LayerManagerComposite::AutoAddMaskEffect, LayerComposite, LayerManagerComposite
20 #include "mozilla/layers/TextureHost.h"  // for CompositingRenderTarget
21 
22 namespace mozilla {
23 namespace layers {
24 
25 // Render aLayer using aCompositor and apply all mask layers of aLayer: The
26 // layer's own mask layer (aLayer->GetMaskLayer()), and any ancestor mask
27 // layers.
28 // If more than one mask layer needs to be applied, we use intermediate surfaces
29 // (CompositingRenderTargets) for rendering, applying one mask layer at a time.
30 // Callers need to provide a callback function aRenderCallback that does the
31 // actual rendering of the source. It needs to have the following form:
32 // void (EffectChain& effectChain, const Rect& clipRect)
33 // aRenderCallback is called exactly once, inside this function, unless aLayer's
34 // visible region is completely clipped out (in that case, aRenderCallback won't
35 // be called at all).
36 // This function calls aLayer->AsHostLayer()->AddBlendModeEffect for the
37 // final rendering pass.
38 //
39 // (This function should really live in LayerManagerComposite.cpp, but we
40 // need to use templates for passing lambdas until bug 1164522 is resolved.)
41 template <typename RenderCallbackType>
RenderWithAllMasks(Layer * aLayer,Compositor * aCompositor,const gfx::IntRect & aClipRect,RenderCallbackType aRenderCallback)42 void RenderWithAllMasks(Layer* aLayer, Compositor* aCompositor,
43                         const gfx::IntRect& aClipRect,
44                         RenderCallbackType aRenderCallback) {
45   Layer* firstMask = nullptr;
46   size_t maskLayerCount = 0;
47   size_t nextAncestorMaskLayer = 0;
48 
49   size_t ancestorMaskLayerCount = aLayer->GetAncestorMaskLayerCount();
50   if (Layer* ownMask = aLayer->GetMaskLayer()) {
51     firstMask = ownMask;
52     maskLayerCount = ancestorMaskLayerCount + 1;
53     nextAncestorMaskLayer = 0;
54   } else if (ancestorMaskLayerCount > 0) {
55     firstMask = aLayer->GetAncestorMaskLayerAt(0);
56     maskLayerCount = ancestorMaskLayerCount;
57     nextAncestorMaskLayer = 1;
58   } else {
59     // no mask layers at all
60   }
61 
62   if (maskLayerCount <= 1) {
63     // This is the common case. Render in one pass and return.
64     EffectChain effectChain(aLayer);
65     LayerManagerComposite::AutoAddMaskEffect autoMaskEffect(firstMask,
66                                                             effectChain);
67     static_cast<LayerComposite*>(aLayer->AsHostLayer())
68         ->AddBlendModeEffect(effectChain);
69     aRenderCallback(effectChain, aClipRect);
70     return;
71   }
72 
73   // We have multiple mask layers.
74   // We split our list of mask layers into three parts:
75   //  (1) The first mask
76   //  (2) The list of intermediate masks (every mask except first and last)
77   //  (3) The final mask.
78   // Part (2) can be empty.
79   // For parts (1) and (2) we need to allocate intermediate surfaces to render
80   // into. The final mask gets rendered into the original render target.
81 
82   // Calculate the size of the intermediate surfaces.
83   gfx::Rect visibleRect(
84       aLayer->GetLocalVisibleRegion().GetBounds().ToUnknownRect());
85   gfx::Matrix4x4 transform = aLayer->GetEffectiveTransform();
86   // TODO: Use RenderTargetIntRect and TransformBy here
87   gfx::IntRect surfaceRect = RoundedOut(
88       transform.TransformAndClipBounds(visibleRect, gfx::Rect(aClipRect)));
89   if (surfaceRect.IsEmpty()) {
90     return;
91   }
92 
93   RefPtr<CompositingRenderTarget> originalTarget =
94       aCompositor->GetCurrentRenderTarget();
95 
96   RefPtr<CompositingRenderTarget> firstTarget =
97       aCompositor->CreateRenderTarget(surfaceRect, INIT_MODE_CLEAR);
98   if (!firstTarget) {
99     return;
100   }
101 
102   // Render the source while applying the first mask.
103   aCompositor->SetRenderTarget(firstTarget);
104   {
105     EffectChain firstEffectChain(aLayer);
106     LayerManagerComposite::AutoAddMaskEffect firstMaskEffect(firstMask,
107                                                              firstEffectChain);
108     aRenderCallback(firstEffectChain, aClipRect - surfaceRect.TopLeft());
109     // firstTarget now contains the transformed source with the first mask and
110     // opacity already applied.
111   }
112 
113   // Apply the intermediate masks.
114   gfx::IntRect intermediateClip(surfaceRect - surfaceRect.TopLeft());
115   RefPtr<CompositingRenderTarget> previousTarget = firstTarget;
116   for (size_t i = nextAncestorMaskLayer; i < ancestorMaskLayerCount - 1; i++) {
117     Layer* intermediateMask = aLayer->GetAncestorMaskLayerAt(i);
118     RefPtr<CompositingRenderTarget> intermediateTarget =
119         aCompositor->CreateRenderTarget(surfaceRect, INIT_MODE_CLEAR);
120     if (!intermediateTarget) {
121       break;
122     }
123     aCompositor->SetRenderTarget(intermediateTarget);
124     EffectChain intermediateEffectChain(aLayer);
125     LayerManagerComposite::AutoAddMaskEffect intermediateMaskEffect(
126         intermediateMask, intermediateEffectChain);
127     if (intermediateMaskEffect.Failed()) {
128       continue;
129     }
130     intermediateEffectChain.mPrimaryEffect =
131         new EffectRenderTarget(previousTarget);
132     aCompositor->DrawQuad(gfx::Rect(surfaceRect), intermediateClip,
133                           intermediateEffectChain, 1.0, gfx::Matrix4x4());
134     previousTarget = intermediateTarget;
135   }
136 
137   aCompositor->SetRenderTarget(originalTarget);
138 
139   // Apply the final mask, rendering into originalTarget.
140   EffectChain finalEffectChain(aLayer);
141   finalEffectChain.mPrimaryEffect = new EffectRenderTarget(previousTarget);
142   Layer* finalMask = aLayer->GetAncestorMaskLayerAt(ancestorMaskLayerCount - 1);
143 
144   // The blend mode needs to be applied in this final step, because this is
145   // where we're blending with the actual background (which is in
146   // originalTarget).
147   static_cast<LayerComposite*>(aLayer->AsHostLayer())
148       ->AddBlendModeEffect(finalEffectChain);
149   LayerManagerComposite::AutoAddMaskEffect autoMaskEffect(finalMask,
150                                                           finalEffectChain);
151   if (!autoMaskEffect.Failed()) {
152     aCompositor->DrawQuad(gfx::Rect(surfaceRect), aClipRect, finalEffectChain,
153                           1.0, gfx::Matrix4x4());
154   }
155 }
156 
157 }  // namespace layers
158 }  // namespace mozilla
159 
160 #endif /* GFX_LayerManagerCompositeUtils_H */
161