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