1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "Snapshot.h"
18 
19 #include "hwui/Canvas.h"
20 
21 namespace android {
22 namespace uirenderer {
23 
24 ///////////////////////////////////////////////////////////////////////////////
25 // Constructors
26 ///////////////////////////////////////////////////////////////////////////////
27 
Snapshot()28 Snapshot::Snapshot()
29         : flags(0)
30         , previous(nullptr)
31         , layer(nullptr)
32         , fbo(0)
33         , invisible(false)
34         , empty(false)
35         , alpha(1.0f)
36         , roundRectClipState(nullptr)
37         , projectionPathMask(nullptr)
38         , mClipArea(&mClipAreaRoot) {
39     transform = &mTransformRoot;
40     region = nullptr;
41 }
42 
43 /**
44  * Copies the specified snapshot/ The specified snapshot is stored as
45  * the previous snapshot.
46  */
Snapshot(Snapshot * s,int saveFlags)47 Snapshot::Snapshot(Snapshot* s, int saveFlags)
48         : flags(0)
49         , previous(s)
50         , layer(s->layer)
51         , fbo(s->fbo)
52         , invisible(s->invisible)
53         , empty(false)
54         , alpha(s->alpha)
55         , roundRectClipState(s->roundRectClipState)
56         , projectionPathMask(s->projectionPathMask)
57         , mClipArea(nullptr)
58         , mViewportData(s->mViewportData)
59         , mRelativeLightCenter(s->mRelativeLightCenter) {
60     if (saveFlags & SaveFlags::Matrix) {
61         mTransformRoot = *s->transform;
62         transform = &mTransformRoot;
63     } else {
64         transform = s->transform;
65     }
66 
67     if (saveFlags & SaveFlags::Clip) {
68         mClipAreaRoot = s->getClipArea();
69         mClipArea = &mClipAreaRoot;
70     } else {
71         mClipArea = s->mClipArea;
72     }
73 
74     if (s->flags & Snapshot::kFlagFboTarget) {
75         flags |= Snapshot::kFlagFboTarget;
76         region = s->region;
77     } else {
78         region = nullptr;
79     }
80 }
81 
82 ///////////////////////////////////////////////////////////////////////////////
83 // Clipping
84 ///////////////////////////////////////////////////////////////////////////////
85 
clipRegionTransformed(const SkRegion & region,SkRegion::Op op)86 void Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) {
87     flags |= Snapshot::kFlagClipSet;
88     mClipArea->clipRegion(region, op);
89 }
90 
clip(const Rect & localClip,SkRegion::Op op)91 void Snapshot::clip(const Rect& localClip, SkRegion::Op op) {
92     flags |= Snapshot::kFlagClipSet;
93     mClipArea->clipRectWithTransform(localClip, transform, op);
94 }
95 
clipPath(const SkPath & path,SkRegion::Op op)96 void Snapshot::clipPath(const SkPath& path, SkRegion::Op op) {
97     flags |= Snapshot::kFlagClipSet;
98     mClipArea->clipPathWithTransform(path, transform, op);
99 }
100 
setClip(float left,float top,float right,float bottom)101 void Snapshot::setClip(float left, float top, float right, float bottom) {
102     flags |= Snapshot::kFlagClipSet;
103     mClipArea->setClip(left, top, right, bottom);
104 }
105 
hasPerspectiveTransform() const106 bool Snapshot::hasPerspectiveTransform() const {
107     return transform->isPerspective();
108 }
109 
getLocalClip()110 const Rect& Snapshot::getLocalClip() {
111     mat4 inverse;
112     inverse.loadInverse(*transform);
113 
114     mLocalClip.set(mClipArea->getClipRect());
115     inverse.mapRect(mLocalClip);
116 
117     return mLocalClip;
118 }
119 
resetClip(float left,float top,float right,float bottom)120 void Snapshot::resetClip(float left, float top, float right, float bottom) {
121     // TODO: This is incorrect, when we start rendering into a new layer,
122     // we may have to modify the previous snapshot's clip rect and clip
123     // region if the previous restore() call did not restore the clip
124     mClipArea = &mClipAreaRoot;
125     setClip(left, top, right, bottom);
126 }
127 
128 ///////////////////////////////////////////////////////////////////////////////
129 // Transforms
130 ///////////////////////////////////////////////////////////////////////////////
131 
resetTransform(float x,float y,float z)132 void Snapshot::resetTransform(float x, float y, float z) {
133 #if HWUI_NEW_OPS
134     LOG_ALWAYS_FATAL("not supported - light center managed differently");
135 #else
136     // before resetting, map current light pos with inverse of current transform
137     Vector3 center = mRelativeLightCenter;
138     mat4 inverse;
139     inverse.loadInverse(*transform);
140     inverse.mapPoint3d(center);
141     mRelativeLightCenter = center;
142 
143     transform = &mTransformRoot;
144     transform->loadTranslate(x, y, z);
145 #endif
146 }
147 
buildScreenSpaceTransform(Matrix4 * outTransform) const148 void Snapshot::buildScreenSpaceTransform(Matrix4* outTransform) const {
149 #if HWUI_NEW_OPS
150     LOG_ALWAYS_FATAL("not supported - not needed by new ops");
151 #else
152     // build (reverse ordered) list of the stack of snapshots, terminated with a NULL
153     Vector<const Snapshot*> snapshotList;
154     snapshotList.push(nullptr);
155     const Snapshot* current = this;
156     do {
157         snapshotList.push(current);
158         current = current->previous;
159     } while (current);
160 
161     // traverse the list, adding in each transform that contributes to the total transform
162     outTransform->loadIdentity();
163     for (size_t i = snapshotList.size() - 1; i > 0; i--) {
164         // iterate down the stack
165         const Snapshot* current = snapshotList[i];
166         const Snapshot* next = snapshotList[i - 1];
167         if (current->flags & kFlagIsFboLayer) {
168             // if we've hit a layer, translate by the layer's draw offset
169             outTransform->translate(current->layer->layer.left, current->layer->layer.top);
170         }
171         if (!next || (next->flags & kFlagIsFboLayer)) {
172             // if this snapshot is last, or if this snapshot is last before an
173             // FBO layer (which reset the transform), apply it
174             outTransform->multiply(*(current->transform));
175         }
176     }
177 #endif
178 }
179 
180 ///////////////////////////////////////////////////////////////////////////////
181 // Clipping round rect
182 ///////////////////////////////////////////////////////////////////////////////
183 
setClippingRoundRect(LinearAllocator & allocator,const Rect & bounds,float radius,bool highPriority)184 void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds,
185         float radius, bool highPriority) {
186     if (bounds.isEmpty()) {
187         mClipArea->setEmpty();
188         return;
189     }
190 
191     if (roundRectClipState && roundRectClipState->highPriority) {
192         // ignore, don't replace, already have a high priority clip
193         return;
194     }
195 
196     RoundRectClipState* state = new (allocator) RoundRectClipState;
197 
198     state->highPriority = highPriority;
199 
200     // store the inverse drawing matrix
201     Matrix4 roundRectDrawingMatrix = getOrthoMatrix();
202     roundRectDrawingMatrix.multiply(*transform);
203     state->matrix.loadInverse(roundRectDrawingMatrix);
204 
205     // compute area under rounded corners - only draws overlapping these rects need to be clipped
206     for (int i = 0 ; i < 4; i++) {
207         state->dangerRects[i] = bounds;
208     }
209     state->dangerRects[0].bottom = state->dangerRects[1].bottom = bounds.top + radius;
210     state->dangerRects[0].right = state->dangerRects[2].right = bounds.left + radius;
211     state->dangerRects[1].left = state->dangerRects[3].left = bounds.right - radius;
212     state->dangerRects[2].top = state->dangerRects[3].top = bounds.bottom - radius;
213     for (int i = 0; i < 4; i++) {
214         transform->mapRect(state->dangerRects[i]);
215 
216         // round danger rects out as though they are AA geometry (since they essentially are)
217         state->dangerRects[i].snapGeometryToPixelBoundaries(true);
218     }
219 
220     // store RR area
221     state->innerRect = bounds;
222     state->innerRect.inset(radius);
223     state->radius = radius;
224 
225     // store as immutable so, for this frame, pointer uniquely identifies this bundle of shader info
226     roundRectClipState = state;
227 }
228 
setProjectionPathMask(LinearAllocator & allocator,const SkPath * path)229 void Snapshot::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) {
230 #if HWUI_NEW_OPS
231     // TODO: remove allocator param for HWUI_NEW_OPS
232     projectionPathMask = path;
233 #else
234     if (path) {
235         ProjectionPathMask* mask = new (allocator) ProjectionPathMask;
236         mask->projectionMask = path;
237         buildScreenSpaceTransform(&(mask->projectionMaskTransform));
238         projectionPathMask = mask;
239     } else {
240         projectionPathMask = nullptr;
241     }
242 #endif
243 }
244 
getClipRoot(Snapshot * target)245 static Snapshot* getClipRoot(Snapshot* target) {
246     while (target->previous && target->previous->previous) {
247         target = target->previous;
248     }
249     return target;
250 }
251 
serializeIntersectedClip(LinearAllocator & allocator,const ClipBase * recordedClip,const Matrix4 & recordedClipTransform)252 const ClipBase* Snapshot::serializeIntersectedClip(LinearAllocator& allocator,
253         const ClipBase* recordedClip, const Matrix4& recordedClipTransform) {
254     auto target = this;
255     if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) {
256         // Clip must be intersected with root, instead of current clip.
257         target = getClipRoot(this);
258     }
259 
260     return target->mClipArea->serializeIntersectedClip(allocator,
261             recordedClip, recordedClipTransform);
262 }
263 
applyClip(const ClipBase * recordedClip,const Matrix4 & transform)264 void Snapshot::applyClip(const ClipBase* recordedClip, const Matrix4& transform) {
265     if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) {
266         // current clip is being replaced, but must intersect with clip root
267         *mClipArea = *(getClipRoot(this)->mClipArea);
268     }
269     mClipArea->applyClip(recordedClip, transform);
270 }
271 
272 ///////////////////////////////////////////////////////////////////////////////
273 // Queries
274 ///////////////////////////////////////////////////////////////////////////////
275 
isIgnored() const276 bool Snapshot::isIgnored() const {
277     return invisible || empty;
278 }
279 
dump() const280 void Snapshot::dump() const {
281     ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d",
282             this, flags, previous, getViewportHeight(), isIgnored(), !mClipArea->isSimple());
283     const Rect& clipRect(mClipArea->getClipRect());
284     ALOGD("  ClipRect %.1f %.1f %.1f %.1f, clip simple %d",
285             clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, mClipArea->isSimple());
286 
287     ALOGD("  Transform (at %p):", transform);
288     transform->dump();
289 }
290 
291 }; // namespace uirenderer
292 }; // namespace android
293