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 #include "ReadbackProcessor.h"
8 #include <sys/types.h>       // for int32_t
9 #include "Layers.h"          // for Layer, PaintedLayer, etc
10 #include "ReadbackLayer.h"   // for ReadbackLayer, ReadbackSink
11 #include "UnitTransforms.h"  // for ViewAs
12 #include "Units.h"           // for ParentLayerIntRect
13 #include "gfxContext.h"      // for gfxContext
14 #include "gfxUtils.h"
15 #include "gfxRect.h"  // for gfxRect
16 #include "mozilla/gfx/2D.h"
17 #include "mozilla/gfx/BasePoint.h"  // for BasePoint
18 #include "mozilla/gfx/BaseRect.h"   // for BaseRect
19 #include "mozilla/gfx/Point.h"      // for Intsize
20 #include "nsDebug.h"                // for NS_ASSERTION
21 #include "nsISupportsImpl.h"        // for gfxContext::Release, etc
22 #include "nsPoint.h"                // for nsIntPoint
23 #include "nsRegion.h"               // for nsIntRegion
24 
25 using namespace mozilla::gfx;
26 
27 namespace mozilla {
28 namespace layers {
29 
BuildUpdates(ContainerLayer * aContainer)30 void ReadbackProcessor::BuildUpdates(ContainerLayer* aContainer) {
31   NS_ASSERTION(mAllUpdates.IsEmpty(), "Some updates not processed?");
32 
33   if (!aContainer->mMayHaveReadbackChild) return;
34 
35   aContainer->mMayHaveReadbackChild = false;
36   // go backwards so the updates read from earlier layers are later in the
37   // array.
38   for (Layer* l = aContainer->GetLastChild(); l; l = l->GetPrevSibling()) {
39     if (l->GetType() == Layer::TYPE_READBACK) {
40       aContainer->mMayHaveReadbackChild = true;
41       BuildUpdatesForLayer(static_cast<ReadbackLayer*>(l));
42     }
43   }
44 }
45 
FindBackgroundLayer(ReadbackLayer * aLayer,nsIntPoint * aOffset)46 static Layer* FindBackgroundLayer(ReadbackLayer* aLayer, nsIntPoint* aOffset) {
47   gfx::Matrix transform;
48   if (!aLayer->GetTransform().Is2D(&transform) ||
49       transform.HasNonIntegerTranslation())
50     return nullptr;
51   nsIntPoint transformOffset(int32_t(transform._31), int32_t(transform._32));
52 
53   for (Layer* l = aLayer->GetPrevSibling(); l; l = l->GetPrevSibling()) {
54     gfx::Matrix backgroundTransform;
55     if (!l->GetTransform().Is2D(&backgroundTransform) ||
56         gfx::ThebesMatrix(backgroundTransform).HasNonIntegerTranslation())
57       return nullptr;
58 
59     nsIntPoint backgroundOffset(int32_t(backgroundTransform._31),
60                                 int32_t(backgroundTransform._32));
61     IntRect rectInBackground(transformOffset - backgroundOffset,
62                              aLayer->GetSize());
63     const nsIntRegion visibleRegion =
64         l->GetLocalVisibleRegion().ToUnknownRegion();
65     if (!visibleRegion.Intersects(rectInBackground)) continue;
66     // Since l is present in the background, from here on we either choose l
67     // or nothing.
68     if (!visibleRegion.Contains(rectInBackground)) return nullptr;
69 
70     if (l->GetEffectiveOpacity() != 1.0 || l->HasMaskLayers() ||
71         !(l->GetContentFlags() & Layer::CONTENT_OPAQUE)) {
72       return nullptr;
73     }
74 
75     // cliprects are post-transform
76     const Maybe<ParentLayerIntRect>& clipRect = l->GetLocalClipRect();
77     if (clipRect && !clipRect->Contains(ViewAs<ParentLayerPixel>(
78                         IntRect(transformOffset, aLayer->GetSize()))))
79       return nullptr;
80 
81     Layer::LayerType type = l->GetType();
82     if (type != Layer::TYPE_COLOR && type != Layer::TYPE_PAINTED)
83       return nullptr;
84 
85     *aOffset = backgroundOffset - transformOffset;
86     return l;
87   }
88 
89   return nullptr;
90 }
91 
BuildUpdatesForLayer(ReadbackLayer * aLayer)92 void ReadbackProcessor::BuildUpdatesForLayer(ReadbackLayer* aLayer) {
93   if (!aLayer->mSink) return;
94 
95   nsIntPoint offset;
96   Layer* newBackground = FindBackgroundLayer(aLayer, &offset);
97   if (!newBackground) {
98     aLayer->SetUnknown();
99     return;
100   }
101 
102   if (newBackground->GetType() == Layer::TYPE_COLOR) {
103     ColorLayer* colorLayer = static_cast<ColorLayer*>(newBackground);
104     if (aLayer->mBackgroundColor != colorLayer->GetColor()) {
105       aLayer->mBackgroundLayer = nullptr;
106       aLayer->mBackgroundColor = colorLayer->GetColor();
107       NS_ASSERTION(aLayer->mBackgroundColor.a == 1.f,
108                    "Color layer said it was opaque!");
109       RefPtr<DrawTarget> dt = aLayer->mSink->BeginUpdate(
110           aLayer->GetRect(), aLayer->AllocateSequenceNumber());
111       if (dt) {
112         ColorPattern color(aLayer->mBackgroundColor);
113         IntSize size = aLayer->GetSize();
114         dt->FillRect(Rect(0, 0, size.width, size.height), color);
115         aLayer->mSink->EndUpdate(aLayer->GetRect());
116       }
117     }
118   } else {
119     NS_ASSERTION(newBackground->AsPaintedLayer(), "Must be PaintedLayer");
120     PaintedLayer* paintedLayer = static_cast<PaintedLayer*>(newBackground);
121     // updateRect is relative to the PaintedLayer
122     IntRect updateRect = aLayer->GetRect() - offset;
123     if (paintedLayer != aLayer->mBackgroundLayer ||
124         offset != aLayer->mBackgroundLayerOffset) {
125       aLayer->mBackgroundLayer = paintedLayer;
126       aLayer->mBackgroundLayerOffset = offset;
127       aLayer->mBackgroundColor = DeviceColor();
128       paintedLayer->SetUsedForReadback(true);
129     } else {
130       nsIntRegion invalid;
131       invalid.Sub(updateRect, paintedLayer->GetValidRegion());
132       updateRect = invalid.GetBounds();
133     }
134 
135     Update update = {aLayer, updateRect, aLayer->AllocateSequenceNumber()};
136     mAllUpdates.AppendElement(update);
137   }
138 }
139 
GetPaintedLayerUpdates(PaintedLayer * aLayer,nsTArray<Update> * aUpdates,nsIntRegion * aUpdateRegion)140 void ReadbackProcessor::GetPaintedLayerUpdates(PaintedLayer* aLayer,
141                                                nsTArray<Update>* aUpdates,
142                                                nsIntRegion* aUpdateRegion) {
143   // All PaintedLayers used for readback are in mAllUpdates (some possibly
144   // with an empty update rect).
145   aLayer->SetUsedForReadback(false);
146   if (aUpdateRegion) {
147     aUpdateRegion->SetEmpty();
148   }
149   for (uint32_t i = mAllUpdates.Length(); i > 0; --i) {
150     const Update& update = mAllUpdates[i - 1];
151     if (update.mLayer->mBackgroundLayer == aLayer) {
152       aLayer->SetUsedForReadback(true);
153       // Don't bother asking for updates if we have an empty update rect.
154       if (!update.mUpdateRect.IsEmpty()) {
155         aUpdates->AppendElement(update);
156         if (aUpdateRegion) {
157           aUpdateRegion->Or(*aUpdateRegion, update.mUpdateRect);
158         }
159       }
160       mAllUpdates.RemoveElementAt(i - 1);
161     }
162   }
163 }
164 
~ReadbackProcessor()165 ReadbackProcessor::~ReadbackProcessor() {
166   for (uint32_t i = mAllUpdates.Length(); i > 0; --i) {
167     const Update& update = mAllUpdates[i - 1];
168     // Unprocessed update. Notify the readback sink that this content is
169     // unknown.
170     update.mLayer->SetUnknown();
171   }
172 }
173 
174 }  // namespace layers
175 }  // namespace mozilla
176