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 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 */ 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 88 bool Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) { 89 flags |= Snapshot::kFlagClipSet; 90 return mClipArea->clipRegion(region, op); 91 } 92 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 98 bool Snapshot::clipPath(const SkPath& path, SkRegion::Op op) { 99 flags |= Snapshot::kFlagClipSet; 100 return mClipArea->clipPathWithTransform(path, transform, op); 101 } 102 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 108 bool Snapshot::hasPerspectiveTransform() const { 109 return transform->isPerspective(); 110 } 111 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 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 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 146 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 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 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 240 bool Snapshot::isIgnored() const { 241 return invisible || empty; 242 } 243 244 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