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