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 #define LOG_TAG "OpenGLRenderer"
18
19 #include "Snapshot.h"
20
21 #include <SkCanvas.h>
22
23 namespace android {
24 namespace uirenderer {
25
26 ///////////////////////////////////////////////////////////////////////////////
27 // Constructors
28 ///////////////////////////////////////////////////////////////////////////////
29
Snapshot()30 Snapshot::Snapshot()
31 : flags(0)
32 , previous(nullptr)
33 , layer(nullptr)
34 , fbo(0)
35 , invisible(false)
36 , empty(false)
37 , alpha(1.0f)
38 , roundRectClipState(nullptr)
39 , projectionPathMask(nullptr)
40 , mClipArea(&mClipAreaRoot) {
41 transform = &mTransformRoot;
42 region = nullptr;
43 }
44
45 /**
46 * Copies the specified snapshot/ The specified snapshot is stored as
47 * the previous snapshot.
48 */
Snapshot(const sp<Snapshot> & s,int saveFlags)49 Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags)
50 : flags(0)
51 , previous(s)
52 , layer(s->layer)
53 , fbo(s->fbo)
54 , invisible(s->invisible)
55 , empty(false)
56 , alpha(s->alpha)
57 , roundRectClipState(s->roundRectClipState)
58 , projectionPathMask(s->projectionPathMask)
59 , mClipArea(nullptr)
60 , mViewportData(s->mViewportData)
61 , mRelativeLightCenter(s->mRelativeLightCenter) {
62 if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
63 mTransformRoot.load(*s->transform);
64 transform = &mTransformRoot;
65 } else {
66 transform = s->transform;
67 }
68
69 if (saveFlags & SkCanvas::kClip_SaveFlag) {
70 mClipAreaRoot = s->getClipArea();
71 mClipArea = &mClipAreaRoot;
72 } else {
73 mClipArea = s->mClipArea;
74 }
75
76 if (s->flags & Snapshot::kFlagFboTarget) {
77 flags |= Snapshot::kFlagFboTarget;
78 region = s->region;
79 } else {
80 region = nullptr;
81 }
82 }
83
84 ///////////////////////////////////////////////////////////////////////////////
85 // Clipping
86 ///////////////////////////////////////////////////////////////////////////////
87
clipRegionTransformed(const SkRegion & region,SkRegion::Op op)88 bool Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) {
89 flags |= Snapshot::kFlagClipSet;
90 return mClipArea->clipRegion(region, op);
91 }
92
clip(float left,float top,float right,float bottom,SkRegion::Op op)93 bool Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) {
94 flags |= Snapshot::kFlagClipSet;
95 return mClipArea->clipRectWithTransform(left, top, right, bottom, transform, op);
96 }
97
clipPath(const SkPath & path,SkRegion::Op op)98 bool Snapshot::clipPath(const SkPath& path, SkRegion::Op op) {
99 flags |= Snapshot::kFlagClipSet;
100 return mClipArea->clipPathWithTransform(path, transform, op);
101 }
102
setClip(float left,float top,float right,float bottom)103 void Snapshot::setClip(float left, float top, float right, float bottom) {
104 mClipArea->setClip(left, top, right, bottom);
105 flags |= Snapshot::kFlagClipSet;
106 }
107
hasPerspectiveTransform() const108 bool Snapshot::hasPerspectiveTransform() const {
109 return transform->isPerspective();
110 }
111
getLocalClip()112 const Rect& Snapshot::getLocalClip() {
113 mat4 inverse;
114 inverse.loadInverse(*transform);
115
116 mLocalClip.set(mClipArea->getClipRect());
117 inverse.mapRect(mLocalClip);
118
119 return mLocalClip;
120 }
121
resetClip(float left,float top,float right,float bottom)122 void Snapshot::resetClip(float left, float top, float right, float bottom) {
123 // TODO: This is incorrect, when we start rendering into a new layer,
124 // we may have to modify the previous snapshot's clip rect and clip
125 // region if the previous restore() call did not restore the clip
126 mClipArea = &mClipAreaRoot;
127 setClip(left, top, right, bottom);
128 }
129
130 ///////////////////////////////////////////////////////////////////////////////
131 // Transforms
132 ///////////////////////////////////////////////////////////////////////////////
133
resetTransform(float x,float y,float z)134 void Snapshot::resetTransform(float x, float y, float z) {
135 // before resetting, map current light pos with inverse of current transform
136 Vector3 center = mRelativeLightCenter;
137 mat4 inverse;
138 inverse.loadInverse(*transform);
139 inverse.mapPoint3d(center);
140 mRelativeLightCenter = center;
141
142 transform = &mTransformRoot;
143 transform->loadTranslate(x, y, z);
144 }
145
buildScreenSpaceTransform(Matrix4 * outTransform) const146 void Snapshot::buildScreenSpaceTransform(Matrix4* outTransform) const {
147 // build (reverse ordered) list of the stack of snapshots, terminated with a NULL
148 Vector<const Snapshot*> snapshotList;
149 snapshotList.push(nullptr);
150 const Snapshot* current = this;
151 do {
152 snapshotList.push(current);
153 current = current->previous.get();
154 } while (current);
155
156 // traverse the list, adding in each transform that contributes to the total transform
157 outTransform->loadIdentity();
158 for (size_t i = snapshotList.size() - 1; i > 0; i--) {
159 // iterate down the stack
160 const Snapshot* current = snapshotList[i];
161 const Snapshot* next = snapshotList[i - 1];
162 if (current->flags & kFlagIsFboLayer) {
163 // if we've hit a layer, translate by the layer's draw offset
164 outTransform->translate(current->layer->layer.left, current->layer->layer.top);
165 }
166 if (!next || (next->flags & kFlagIsFboLayer)) {
167 // if this snapshot is last, or if this snapshot is last before an
168 // FBO layer (which reset the transform), apply it
169 outTransform->multiply(*(current->transform));
170 }
171 }
172 }
173
174 ///////////////////////////////////////////////////////////////////////////////
175 // Clipping round rect
176 ///////////////////////////////////////////////////////////////////////////////
177
setClippingRoundRect(LinearAllocator & allocator,const Rect & bounds,float radius,bool highPriority)178 void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds,
179 float radius, bool highPriority) {
180 if (bounds.isEmpty()) {
181 mClipArea->setEmpty();
182 return;
183 }
184
185 if (roundRectClipState && roundRectClipState->highPriority) {
186 // ignore, don't replace, already have a high priority clip
187 return;
188 }
189
190 RoundRectClipState* state = new (allocator) RoundRectClipState;
191
192 state->highPriority = highPriority;
193
194 // store the inverse drawing matrix
195 Matrix4 roundRectDrawingMatrix;
196 roundRectDrawingMatrix.load(getOrthoMatrix());
197 roundRectDrawingMatrix.multiply(*transform);
198 state->matrix.loadInverse(roundRectDrawingMatrix);
199
200 // compute area under rounded corners - only draws overlapping these rects need to be clipped
201 for (int i = 0 ; i < 4; i++) {
202 state->dangerRects[i] = bounds;
203 }
204 state->dangerRects[0].bottom = state->dangerRects[1].bottom = bounds.top + radius;
205 state->dangerRects[0].right = state->dangerRects[2].right = bounds.left + radius;
206 state->dangerRects[1].left = state->dangerRects[3].left = bounds.right - radius;
207 state->dangerRects[2].top = state->dangerRects[3].top = bounds.bottom - radius;
208 for (int i = 0; i < 4; i++) {
209 transform->mapRect(state->dangerRects[i]);
210
211 // round danger rects out as though they are AA geometry (since they essentially are)
212 state->dangerRects[i].snapGeometryToPixelBoundaries(true);
213 }
214
215 // store RR area
216 state->innerRect = bounds;
217 state->innerRect.inset(radius);
218 state->radius = radius;
219
220 // store as immutable so, for this frame, pointer uniquely identifies this bundle of shader info
221 roundRectClipState = state;
222 }
223
setProjectionPathMask(LinearAllocator & allocator,const SkPath * path)224 void Snapshot::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) {
225 if (path) {
226 ProjectionPathMask* mask = new (allocator) ProjectionPathMask;
227 mask->projectionMask = path;
228 buildScreenSpaceTransform(&(mask->projectionMaskTransform));
229
230 projectionPathMask = mask;
231 } else {
232 projectionPathMask = nullptr;
233 }
234 }
235
236 ///////////////////////////////////////////////////////////////////////////////
237 // Queries
238 ///////////////////////////////////////////////////////////////////////////////
239
isIgnored() const240 bool Snapshot::isIgnored() const {
241 return invisible || empty;
242 }
243
dump() const244 void Snapshot::dump() const {
245 ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d",
246 this, flags, previous.get(), getViewportHeight(), isIgnored(), !mClipArea->isSimple());
247 const Rect& clipRect(mClipArea->getClipRect());
248 ALOGD(" ClipRect %.1f %.1f %.1f %.1f, clip simple %d",
249 clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, mClipArea->isSimple());
250
251 ALOGD(" Transform (at %p):", transform);
252 transform->dump();
253 }
254
255 }; // namespace uirenderer
256 }; // namespace android
257