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