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 28 Snapshot::Snapshot() 29 : flags(0) 30 , previous(nullptr) 31 , layer(nullptr) 32 , fbo(0) 33 , alpha(1.0f) 34 , roundRectClipState(nullptr) 35 , projectionPathMask(nullptr) 36 , mClipArea(&mClipAreaRoot) { 37 transform = &mTransformRoot; 38 mRelativeLightCenter.x = mRelativeLightCenter.y = mRelativeLightCenter.z = 0; 39 } 40 41 /** 42 * Copies the specified snapshot/ The specified snapshot is stored as 43 * the previous snapshot. 44 */ 45 Snapshot::Snapshot(Snapshot* s, int saveFlags) 46 : flags(0) 47 , previous(s) 48 , layer(s->layer) 49 , fbo(s->fbo) 50 , alpha(s->alpha) 51 , roundRectClipState(s->roundRectClipState) 52 , projectionPathMask(s->projectionPathMask) 53 , mClipArea(nullptr) 54 , mViewportData(s->mViewportData) 55 , mRelativeLightCenter(s->mRelativeLightCenter) { 56 if (saveFlags & SaveFlags::Matrix) { 57 mTransformRoot = *s->transform; 58 transform = &mTransformRoot; 59 } else { 60 transform = s->transform; 61 } 62 63 if (saveFlags & SaveFlags::Clip) { 64 mClipAreaRoot = s->getClipArea(); 65 mClipArea = &mClipAreaRoot; 66 } else { 67 mClipArea = s->mClipArea; 68 } 69 } 70 71 /////////////////////////////////////////////////////////////////////////////// 72 // Clipping 73 /////////////////////////////////////////////////////////////////////////////// 74 75 void Snapshot::clip(const Rect& localClip, SkClipOp op) { 76 flags |= Snapshot::kFlagClipSet; 77 mClipArea->clipRectWithTransform(localClip, transform, static_cast<SkRegion::Op>(op)); 78 } 79 80 void Snapshot::clipPath(const SkPath& path, SkClipOp op) { 81 flags |= Snapshot::kFlagClipSet; 82 mClipArea->clipPathWithTransform(path, transform, static_cast<SkRegion::Op>(op)); 83 } 84 85 void Snapshot::setClip(float left, float top, float right, float bottom) { 86 flags |= Snapshot::kFlagClipSet; 87 mClipArea->setClip(left, top, right, bottom); 88 } 89 90 bool Snapshot::hasPerspectiveTransform() const { 91 return transform->isPerspective(); 92 } 93 94 const Rect& Snapshot::getLocalClip() { 95 mat4 inverse; 96 inverse.loadInverse(*transform); 97 98 mLocalClip.set(mClipArea->getClipRect()); 99 inverse.mapRect(mLocalClip); 100 101 return mLocalClip; 102 } 103 104 void Snapshot::resetClip(float left, float top, float right, float bottom) { 105 // TODO: This is incorrect, when we start rendering into a new layer, 106 // we may have to modify the previous snapshot's clip rect and clip 107 // region if the previous restore() call did not restore the clip 108 mClipArea = &mClipAreaRoot; 109 setClip(left, top, right, bottom); 110 } 111 112 /////////////////////////////////////////////////////////////////////////////// 113 // Clipping round rect 114 /////////////////////////////////////////////////////////////////////////////// 115 116 void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, 117 float radius, bool highPriority) { 118 if (bounds.isEmpty()) { 119 mClipArea->setEmpty(); 120 return; 121 } 122 123 if (roundRectClipState && roundRectClipState->highPriority) { 124 // ignore, don't replace, already have a high priority clip 125 return; 126 } 127 128 RoundRectClipState* state = new (allocator) RoundRectClipState; 129 130 state->highPriority = highPriority; 131 132 // store the inverse drawing matrix 133 Matrix4 roundRectDrawingMatrix = getOrthoMatrix(); 134 roundRectDrawingMatrix.multiply(*transform); 135 state->matrix.loadInverse(roundRectDrawingMatrix); 136 137 // compute area under rounded corners - only draws overlapping these rects need to be clipped 138 for (int i = 0 ; i < 4; i++) { 139 state->dangerRects[i] = bounds; 140 } 141 state->dangerRects[0].bottom = state->dangerRects[1].bottom = bounds.top + radius; 142 state->dangerRects[0].right = state->dangerRects[2].right = bounds.left + radius; 143 state->dangerRects[1].left = state->dangerRects[3].left = bounds.right - radius; 144 state->dangerRects[2].top = state->dangerRects[3].top = bounds.bottom - radius; 145 for (int i = 0; i < 4; i++) { 146 transform->mapRect(state->dangerRects[i]); 147 148 // round danger rects out as though they are AA geometry (since they essentially are) 149 state->dangerRects[i].snapGeometryToPixelBoundaries(true); 150 } 151 152 // store RR area 153 state->innerRect = bounds; 154 state->innerRect.inset(radius); 155 state->radius = radius; 156 157 // store as immutable so, for this frame, pointer uniquely identifies this bundle of shader info 158 roundRectClipState = state; 159 } 160 161 void Snapshot::setProjectionPathMask(const SkPath* path) { 162 projectionPathMask = path; 163 } 164 165 static Snapshot* getClipRoot(Snapshot* target) { 166 while (target->previous && target->previous->previous) { 167 target = target->previous; 168 } 169 return target; 170 } 171 172 const ClipBase* Snapshot::serializeIntersectedClip(LinearAllocator& allocator, 173 const ClipBase* recordedClip, const Matrix4& recordedClipTransform) { 174 auto target = this; 175 if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) { 176 // Clip must be intersected with root, instead of current clip. 177 target = getClipRoot(this); 178 } 179 180 return target->mClipArea->serializeIntersectedClip(allocator, 181 recordedClip, recordedClipTransform); 182 } 183 184 void Snapshot::applyClip(const ClipBase* recordedClip, const Matrix4& transform) { 185 if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) { 186 // current clip is being replaced, but must intersect with clip root 187 *mClipArea = *(getClipRoot(this)->mClipArea); 188 } 189 mClipArea->applyClip(recordedClip, transform); 190 } 191 192 /////////////////////////////////////////////////////////////////////////////// 193 // Queries 194 /////////////////////////////////////////////////////////////////////////////// 195 196 void Snapshot::dump() const { 197 ALOGD("Snapshot %p, flags %x, prev %p, height %d, hasComplexClip %d", 198 this, flags, previous, getViewportHeight(), !mClipArea->isSimple()); 199 const Rect& clipRect(mClipArea->getClipRect()); 200 ALOGD(" ClipRect %.1f %.1f %.1f %.1f, clip simple %d", 201 clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, mClipArea->isSimple()); 202 203 ALOGD(" Transform (at %p):", transform); 204 transform->dump(); 205 } 206 207 }; // namespace uirenderer 208 }; // namespace android 209