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