Home | History | Annotate | Download | only in hwui
      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 #include "CanvasState.h"
     18 #include "hwui/Canvas.h"
     19 #include "utils/MathUtils.h"
     20 
     21 namespace android {
     22 namespace uirenderer {
     23 
     24 CanvasState::CanvasState(CanvasStateClient& renderer)
     25         : mWidth(-1), mHeight(-1), mSaveCount(1), mCanvas(renderer), mSnapshot(&mFirstSnapshot) {}
     26 
     27 CanvasState::~CanvasState() {
     28     // First call freeSnapshot on all but mFirstSnapshot
     29     // to invoke all the dtors
     30     freeAllSnapshots();
     31 
     32     // Now actually release the memory
     33     while (mSnapshotPool) {
     34         void* temp = mSnapshotPool;
     35         mSnapshotPool = mSnapshotPool->previous;
     36         free(temp);
     37     }
     38 }
     39 
     40 void CanvasState::initializeRecordingSaveStack(int viewportWidth, int viewportHeight) {
     41     if (mWidth != viewportWidth || mHeight != viewportHeight) {
     42         mWidth = viewportWidth;
     43         mHeight = viewportHeight;
     44         mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight);
     45         mCanvas.onViewportInitialized();
     46     }
     47 
     48     freeAllSnapshots();
     49     mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip);
     50     mSnapshot->setRelativeLightCenter(Vector3());
     51     mSaveCount = 1;
     52 }
     53 
     54 void CanvasState::initializeSaveStack(int viewportWidth, int viewportHeight, float clipLeft,
     55                                       float clipTop, float clipRight, float clipBottom,
     56                                       const Vector3& lightCenter) {
     57     if (mWidth != viewportWidth || mHeight != viewportHeight) {
     58         mWidth = viewportWidth;
     59         mHeight = viewportHeight;
     60         mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight);
     61         mCanvas.onViewportInitialized();
     62     }
     63 
     64     freeAllSnapshots();
     65     mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip);
     66     mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
     67     mSnapshot->fbo = mCanvas.getTargetFbo();
     68     mSnapshot->setRelativeLightCenter(lightCenter);
     69     mSaveCount = 1;
     70 }
     71 
     72 Snapshot* CanvasState::allocSnapshot(Snapshot* previous, int savecount) {
     73     void* memory;
     74     if (mSnapshotPool) {
     75         memory = mSnapshotPool;
     76         mSnapshotPool = mSnapshotPool->previous;
     77         mSnapshotPoolCount--;
     78     } else {
     79         memory = malloc(sizeof(Snapshot));
     80     }
     81     return new (memory) Snapshot(previous, savecount);
     82 }
     83 
     84 void CanvasState::freeSnapshot(Snapshot* snapshot) {
     85     snapshot->~Snapshot();
     86     // Arbitrary number, just don't let this grown unbounded
     87     if (mSnapshotPoolCount > 10) {
     88         free((void*)snapshot);
     89     } else {
     90         snapshot->previous = mSnapshotPool;
     91         mSnapshotPool = snapshot;
     92         mSnapshotPoolCount++;
     93     }
     94 }
     95 
     96 void CanvasState::freeAllSnapshots() {
     97     while (mSnapshot != &mFirstSnapshot) {
     98         Snapshot* temp = mSnapshot;
     99         mSnapshot = mSnapshot->previous;
    100         freeSnapshot(temp);
    101     }
    102 }
    103 
    104 ///////////////////////////////////////////////////////////////////////////////
    105 // Save (layer)
    106 ///////////////////////////////////////////////////////////////////////////////
    107 
    108 /**
    109  * Guaranteed to save without side-effects
    110  *
    111  * This approach, here and in restoreSnapshot(), allows subclasses to directly manipulate the save
    112  * stack, and ensures restoreToCount() doesn't call back into subclass overrides.
    113  */
    114 int CanvasState::saveSnapshot(int flags) {
    115     mSnapshot = allocSnapshot(mSnapshot, flags);
    116     return mSaveCount++;
    117 }
    118 
    119 int CanvasState::save(int flags) {
    120     return saveSnapshot(flags);
    121 }
    122 
    123 /**
    124  * Guaranteed to restore without side-effects.
    125  */
    126 void CanvasState::restoreSnapshot() {
    127     Snapshot* toRemove = mSnapshot;
    128     Snapshot* toRestore = mSnapshot->previous;
    129 
    130     mSaveCount--;
    131     mSnapshot = toRestore;
    132 
    133     // subclass handles restore implementation
    134     mCanvas.onSnapshotRestored(*toRemove, *toRestore);
    135 
    136     freeSnapshot(toRemove);
    137 }
    138 
    139 void CanvasState::restore() {
    140     if (mSaveCount > 1) {
    141         restoreSnapshot();
    142     }
    143 }
    144 
    145 void CanvasState::restoreToCount(int saveCount) {
    146     if (saveCount < 1) saveCount = 1;
    147 
    148     while (mSaveCount > saveCount) {
    149         restoreSnapshot();
    150     }
    151 }
    152 
    153 ///////////////////////////////////////////////////////////////////////////////
    154 // Matrix
    155 ///////////////////////////////////////////////////////////////////////////////
    156 
    157 void CanvasState::getMatrix(SkMatrix* matrix) const {
    158     mSnapshot->transform->copyTo(*matrix);
    159 }
    160 
    161 void CanvasState::translate(float dx, float dy, float dz) {
    162     mSnapshot->transform->translate(dx, dy, dz);
    163 }
    164 
    165 void CanvasState::rotate(float degrees) {
    166     mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f);
    167 }
    168 
    169 void CanvasState::scale(float sx, float sy) {
    170     mSnapshot->transform->scale(sx, sy, 1.0f);
    171 }
    172 
    173 void CanvasState::skew(float sx, float sy) {
    174     mSnapshot->transform->skew(sx, sy);
    175 }
    176 
    177 void CanvasState::setMatrix(const SkMatrix& matrix) {
    178     mSnapshot->transform->load(matrix);
    179 }
    180 
    181 void CanvasState::setMatrix(const Matrix4& matrix) {
    182     *(mSnapshot->transform) = matrix;
    183 }
    184 
    185 void CanvasState::concatMatrix(const SkMatrix& matrix) {
    186     mat4 transform(matrix);
    187     mSnapshot->transform->multiply(transform);
    188 }
    189 
    190 void CanvasState::concatMatrix(const Matrix4& matrix) {
    191     mSnapshot->transform->multiply(matrix);
    192 }
    193 
    194 ///////////////////////////////////////////////////////////////////////////////
    195 // Clip
    196 ///////////////////////////////////////////////////////////////////////////////
    197 
    198 bool CanvasState::clipRect(float left, float top, float right, float bottom, SkClipOp op) {
    199     mSnapshot->clip(Rect(left, top, right, bottom), op);
    200     return !mSnapshot->clipIsEmpty();
    201 }
    202 
    203 bool CanvasState::clipPath(const SkPath* path, SkClipOp op) {
    204     mSnapshot->clipPath(*path, op);
    205     return !mSnapshot->clipIsEmpty();
    206 }
    207 
    208 void CanvasState::setClippingOutline(LinearAllocator& allocator, const Outline* outline) {
    209     Rect bounds;
    210     float radius;
    211     if (!outline->getAsRoundRect(&bounds, &radius)) return;  // only RR supported
    212 
    213     bool outlineIsRounded = MathUtils::isPositive(radius);
    214     if (!outlineIsRounded || currentTransform()->isSimple()) {
    215         // TODO: consider storing this rect separately, so that this can't be replaced with clip ops
    216         clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, SkClipOp::kIntersect);
    217     }
    218     if (outlineIsRounded) {
    219         setClippingRoundRect(allocator, bounds, radius, false);
    220     }
    221 }
    222 
    223 ///////////////////////////////////////////////////////////////////////////////
    224 // Quick Rejection
    225 ///////////////////////////////////////////////////////////////////////////////
    226 
    227 /**
    228  * Calculates whether content drawn within the passed bounds would be outside of, or intersect with
    229  * the clipRect. Does not modify the scissor.
    230  *
    231  * @param clipRequired if not null, will be set to true if element intersects clip
    232  *         (and wasn't rejected)
    233  *
    234  * @param snapOut if set, the geometry will be treated as having an AA ramp.
    235  *         See Rect::snapGeometryToPixelBoundaries()
    236  */
    237 bool CanvasState::calculateQuickRejectForScissor(float left, float top, float right, float bottom,
    238                                                  bool* clipRequired, bool* roundRectClipRequired,
    239                                                  bool snapOut) const {
    240     if (bottom <= top || right <= left) {
    241         return true;
    242     }
    243 
    244     Rect r(left, top, right, bottom);
    245     currentTransform()->mapRect(r);
    246     r.snapGeometryToPixelBoundaries(snapOut);
    247 
    248     Rect clipRect(currentRenderTargetClip());
    249     clipRect.snapToPixelBoundaries();
    250 
    251     if (!clipRect.intersects(r)) return true;
    252 
    253     // clip is required if geometry intersects clip rect
    254     if (clipRequired) {
    255         *clipRequired = !clipRect.contains(r);
    256     }
    257 
    258     // round rect clip is required if RR clip exists, and geometry intersects its corners
    259     if (roundRectClipRequired) {
    260         *roundRectClipRequired = mSnapshot->roundRectClipState != nullptr &&
    261                                  mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r);
    262     }
    263     return false;
    264 }
    265 
    266 bool CanvasState::quickRejectConservative(float left, float top, float right, float bottom) const {
    267     if (bottom <= top || right <= left) {
    268         return true;
    269     }
    270 
    271     Rect r(left, top, right, bottom);
    272     currentTransform()->mapRect(r);
    273     r.roundOut();  // rounded out to be conservative
    274 
    275     Rect clipRect(currentRenderTargetClip());
    276     clipRect.snapToPixelBoundaries();
    277 
    278     if (!clipRect.intersects(r)) return true;
    279 
    280     return false;
    281 }
    282 
    283 }  // namespace uirenderer
    284 }  // namespace android
    285