StatefulBaseRenderer.cpp revision 62d307c2402777d5e53b4590af5f32f8c55afd81
1/* 2 * Copyright (C) 2014 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 <SkCanvas.h> 20 21#include "StatefulBaseRenderer.h" 22 23namespace android { 24namespace uirenderer { 25 26StatefulBaseRenderer::StatefulBaseRenderer() 27 : mDirtyClip(false) 28 , mWidth(-1) 29 , mHeight(-1) 30 , mSaveCount(1) 31 , mFirstSnapshot(new Snapshot) 32 , mSnapshot(mFirstSnapshot) { 33} 34 35void StatefulBaseRenderer::initializeSaveStack(float clipLeft, float clipTop, 36 float clipRight, float clipBottom) { 37 mSnapshot = new Snapshot(mFirstSnapshot, 38 SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); 39 mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom); 40 mSnapshot->fbo = getTargetFbo(); 41 mSaveCount = 1; 42} 43 44void StatefulBaseRenderer::setViewport(int width, int height) { 45 mWidth = width; 46 mHeight = height; 47 mFirstSnapshot->initializeViewport(width, height); 48 onViewportInitialized(); 49} 50 51/////////////////////////////////////////////////////////////////////////////// 52// Save (layer) 53/////////////////////////////////////////////////////////////////////////////// 54 55/** 56 * Non-virtual implementation of save, guaranteed to save without side-effects 57 * 58 * The approach here and in restoreSnapshot(), allows subclasses to directly manipulate the save 59 * stack, and ensures restoreToCount() doesn't call back into subclass overrides. 60 */ 61int StatefulBaseRenderer::saveSnapshot(int flags) { 62 mSnapshot = new Snapshot(mSnapshot, flags); 63 return mSaveCount++; 64} 65 66int StatefulBaseRenderer::save(int flags) { 67 return saveSnapshot(flags); 68} 69 70/** 71 * Non-virtual implementation of restore, guaranteed to restore without side-effects. 72 */ 73void StatefulBaseRenderer::restoreSnapshot() { 74 sp<Snapshot> toRemove = mSnapshot; 75 sp<Snapshot> toRestore = mSnapshot->previous; 76 77 mSaveCount--; 78 mSnapshot = toRestore; 79 80 // subclass handles restore implementation 81 onSnapshotRestored(*toRemove, *toRestore); 82} 83 84void StatefulBaseRenderer::restore() { 85 if (mSaveCount > 1) { 86 restoreSnapshot(); 87 } 88} 89 90void StatefulBaseRenderer::restoreToCount(int saveCount) { 91 if (saveCount < 1) saveCount = 1; 92 93 while (mSaveCount > saveCount) { 94 restoreSnapshot(); 95 } 96} 97 98/////////////////////////////////////////////////////////////////////////////// 99// Matrix 100/////////////////////////////////////////////////////////////////////////////// 101 102void StatefulBaseRenderer::getMatrix(Matrix4* matrix) const { 103 matrix->load(*(mSnapshot->transform)); 104} 105 106void StatefulBaseRenderer::getMatrix(SkMatrix* matrix) const { 107 mSnapshot->transform->copyTo(*matrix); 108} 109 110void StatefulBaseRenderer::translate(float dx, float dy, float dz) { 111 mSnapshot->transform->translate(dx, dy, dz); 112} 113 114void StatefulBaseRenderer::rotate(float degrees) { 115 mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f); 116} 117 118void StatefulBaseRenderer::scale(float sx, float sy) { 119 mSnapshot->transform->scale(sx, sy, 1.0f); 120} 121 122void StatefulBaseRenderer::skew(float sx, float sy) { 123 mSnapshot->transform->skew(sx, sy); 124} 125 126void StatefulBaseRenderer::setMatrix(const SkMatrix& matrix) { 127 mSnapshot->transform->load(matrix); 128} 129 130void StatefulBaseRenderer::setMatrix(const Matrix4& matrix) { 131 mSnapshot->transform->load(matrix); 132} 133 134void StatefulBaseRenderer::concatMatrix(const SkMatrix& matrix) { 135 mat4 transform(matrix); 136 mSnapshot->transform->multiply(transform); 137} 138 139void StatefulBaseRenderer::concatMatrix(const Matrix4& matrix) { 140 mSnapshot->transform->multiply(matrix); 141} 142 143/////////////////////////////////////////////////////////////////////////////// 144// Clip 145/////////////////////////////////////////////////////////////////////////////// 146 147bool StatefulBaseRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { 148 if (CC_LIKELY(currentTransform()->rectToRect())) { 149 mDirtyClip |= mSnapshot->clip(left, top, right, bottom, op); 150 return !mSnapshot->clipRect->isEmpty(); 151 } 152 153 SkPath path; 154 path.addRect(left, top, right, bottom); 155 156 return StatefulBaseRenderer::clipPath(&path, op); 157} 158 159bool StatefulBaseRenderer::clipPath(const SkPath* path, SkRegion::Op op) { 160 SkMatrix transform; 161 currentTransform()->copyTo(transform); 162 163 SkPath transformed; 164 path->transform(transform, &transformed); 165 166 SkRegion clip; 167 if (!mSnapshot->previous->clipRegion->isEmpty()) { 168 clip.setRegion(*mSnapshot->previous->clipRegion); 169 } else { 170 if (mSnapshot->previous == firstSnapshot()) { 171 clip.setRect(0, 0, getWidth(), getHeight()); 172 } else { 173 Rect* bounds = mSnapshot->previous->clipRect; 174 clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom); 175 } 176 } 177 178 SkRegion region; 179 region.setPath(transformed, clip); 180 181 // region is the transformed input path, masked by the previous clip 182 mDirtyClip |= mSnapshot->clipRegionTransformed(region, op); 183 return !mSnapshot->clipRect->isEmpty(); 184} 185 186bool StatefulBaseRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) { 187 mDirtyClip |= mSnapshot->clipRegionTransformed(*region, op); 188 return !mSnapshot->clipRect->isEmpty(); 189} 190 191void StatefulBaseRenderer::setClippingOutline(LinearAllocator& allocator, const Outline* outline) { 192 mSnapshot->setClippingOutline(allocator, outline); 193} 194 195/////////////////////////////////////////////////////////////////////////////// 196// Quick Rejection 197/////////////////////////////////////////////////////////////////////////////// 198 199/** 200 * Calculates whether content drawn within the passed bounds would be outside of, or intersect with 201 * the clipRect. Does not modify the scissor. 202 * 203 * @param clipRequired if not null, will be set to true if element intersects clip 204 * (and wasn't rejected) 205 * 206 * @param snapOut if set, the geometry will be treated as having an AA ramp. 207 * See Rect::snapGeometryToPixelBoundaries() 208 */ 209bool StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top, 210 float right, float bottom, 211 bool* clipRequired, bool* roundRectClipRequired, 212 bool snapOut) const { 213 if (mSnapshot->isIgnored() || bottom <= top || right <= left) { 214 return true; 215 } 216 217 Rect r(left, top, right, bottom); 218 currentTransform()->mapRect(r); 219 r.snapGeometryToPixelBoundaries(snapOut); 220 221 Rect clipRect(*currentClipRect()); 222 clipRect.snapToPixelBoundaries(); 223 224 if (!clipRect.intersects(r)) return true; 225 226 // clip is required if geometry intersects clip rect 227 if (clipRequired) { 228 *clipRequired = !clipRect.contains(r); 229 } 230 231 // round rect clip is required if RR clip exists, and geometry intersects its corners 232 if (roundRectClipRequired) { 233 *roundRectClipRequired = mSnapshot->roundRectClipState != NULL 234 && mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r); 235 } 236 return false; 237} 238 239/** 240 * Returns false if drawing won't be clipped out. 241 * 242 * Makes the decision conservatively, by rounding out the mapped rect before comparing with the 243 * clipRect. To be used when perfect, pixel accuracy is not possible (esp. with tessellation) but 244 * rejection is still desired. 245 * 246 * This function, unlike quickRejectSetupScissor, should be used where precise geometry information 247 * isn't known (esp. when geometry adjusts based on scale). Generally, this will be first pass 248 * rejection where precise rejection isn't important, or precise information isn't available. 249 */ 250bool StatefulBaseRenderer::quickRejectConservative(float left, float top, 251 float right, float bottom) const { 252 if (mSnapshot->isIgnored() || bottom <= top || right <= left) { 253 return true; 254 } 255 256 Rect r(left, top, right, bottom); 257 currentTransform()->mapRect(r); 258 r.roundOut(); // rounded out to be conservative 259 260 Rect clipRect(*currentClipRect()); 261 clipRect.snapToPixelBoundaries(); 262 263 if (!clipRect.intersects(r)) return true; 264 265 return false; 266} 267 268}; // namespace uirenderer 269}; // namespace android 270