Home | History | Annotate | Download | only in hwui
      1 /*
      2  * Copyright (C) 2015 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 #include "ClipArea.h"
     17 
     18 #include <SkPath.h>
     19 #include <limits>
     20 
     21 #include "Rect.h"
     22 
     23 namespace android {
     24 namespace uirenderer {
     25 
     26 static bool intersect(Rect& r, const Rect& r2) {
     27     bool hasIntersection = r.intersect(r2);
     28     if (!hasIntersection) {
     29         r.setEmpty();
     30     }
     31     return hasIntersection;
     32 }
     33 
     34 static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) {
     35     Vertex v;
     36     v.x = x;
     37     v.y = y;
     38     transform.mapPoint(v.x, v.y);
     39     transformedBounds.expandToCoverVertex(v.x, v.y);
     40 }
     41 
     42 Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform) {
     43     const float kMinFloat = std::numeric_limits<float>::lowest();
     44     const float kMaxFloat = std::numeric_limits<float>::max();
     45     Rect transformedBounds = { kMaxFloat, kMaxFloat, kMinFloat, kMinFloat };
     46     handlePoint(transformedBounds, transform, r.left, r.top);
     47     handlePoint(transformedBounds, transform, r.right, r.top);
     48     handlePoint(transformedBounds, transform, r.left, r.bottom);
     49     handlePoint(transformedBounds, transform, r.right, r.bottom);
     50     return transformedBounds;
     51 }
     52 
     53 /*
     54  * TransformedRectangle
     55  */
     56 
     57 TransformedRectangle::TransformedRectangle() {
     58 }
     59 
     60 TransformedRectangle::TransformedRectangle(const Rect& bounds,
     61         const Matrix4& transform)
     62         : mBounds(bounds)
     63         , mTransform(transform) {
     64 }
     65 
     66 bool TransformedRectangle::canSimplyIntersectWith(
     67         const TransformedRectangle& other) const {
     68 
     69     return mTransform == other.mTransform;
     70 }
     71 
     72 bool TransformedRectangle::intersectWith(const TransformedRectangle& other) {
     73     Rect translatedBounds(other.mBounds);
     74     return intersect(mBounds, translatedBounds);
     75 }
     76 
     77 bool TransformedRectangle::isEmpty() const {
     78     return mBounds.isEmpty();
     79 }
     80 
     81 /*
     82  * RectangleList
     83  */
     84 
     85 RectangleList::RectangleList()
     86         : mTransformedRectanglesCount(0) {
     87 }
     88 
     89 bool RectangleList::isEmpty() const {
     90     if (mTransformedRectanglesCount < 1) {
     91         return true;
     92     }
     93 
     94     for (int i = 0; i < mTransformedRectanglesCount; i++) {
     95         if (mTransformedRectangles[i].isEmpty()) {
     96             return true;
     97         }
     98     }
     99     return false;
    100 }
    101 
    102 int RectangleList::getTransformedRectanglesCount() const {
    103     return mTransformedRectanglesCount;
    104 }
    105 
    106 const TransformedRectangle& RectangleList::getTransformedRectangle(int i) const {
    107     return mTransformedRectangles[i];
    108 }
    109 
    110 void RectangleList::setEmpty() {
    111     mTransformedRectanglesCount = 0;
    112 }
    113 
    114 void RectangleList::set(const Rect& bounds, const Matrix4& transform) {
    115     mTransformedRectanglesCount = 1;
    116     mTransformedRectangles[0] = TransformedRectangle(bounds, transform);
    117 }
    118 
    119 bool RectangleList::intersectWith(const Rect& bounds,
    120         const Matrix4& transform) {
    121     TransformedRectangle newRectangle(bounds, transform);
    122 
    123     // Try to find a rectangle with a compatible transformation
    124     int index = 0;
    125     for (; index < mTransformedRectanglesCount; index++) {
    126         TransformedRectangle& tr(mTransformedRectangles[index]);
    127         if (tr.canSimplyIntersectWith(newRectangle)) {
    128             tr.intersectWith(newRectangle);
    129             return true;
    130         }
    131     }
    132 
    133     // Add it to the list if there is room
    134     if (index < kMaxTransformedRectangles) {
    135         mTransformedRectangles[index] = newRectangle;
    136         mTransformedRectanglesCount += 1;
    137         return true;
    138     }
    139 
    140     // This rectangle list is full
    141     return false;
    142 }
    143 
    144 Rect RectangleList::calculateBounds() const {
    145     Rect bounds;
    146     for (int index = 0; index < mTransformedRectanglesCount; index++) {
    147         const TransformedRectangle& tr(mTransformedRectangles[index]);
    148         if (index == 0) {
    149             bounds = tr.transformedBounds();
    150         } else {
    151             bounds.intersect(tr.transformedBounds());
    152         }
    153     }
    154     return bounds;
    155 }
    156 
    157 static SkPath pathFromTransformedRectangle(const Rect& bounds,
    158         const Matrix4& transform) {
    159     SkPath rectPath;
    160     SkPath rectPathTransformed;
    161     rectPath.addRect(bounds.left, bounds.top, bounds.right, bounds.bottom);
    162     SkMatrix skTransform;
    163     transform.copyTo(skTransform);
    164     rectPath.transform(skTransform, &rectPathTransformed);
    165     return rectPathTransformed;
    166 }
    167 
    168 SkRegion RectangleList::convertToRegion(const SkRegion& clip) const {
    169     SkRegion rectangleListAsRegion;
    170     for (int index = 0; index < mTransformedRectanglesCount; index++) {
    171         const TransformedRectangle& tr(mTransformedRectangles[index]);
    172         SkPath rectPathTransformed = pathFromTransformedRectangle(
    173                 tr.getBounds(), tr.getTransform());
    174         if (index == 0) {
    175             rectangleListAsRegion.setPath(rectPathTransformed, clip);
    176         } else {
    177             SkRegion rectRegion;
    178             rectRegion.setPath(rectPathTransformed, clip);
    179             rectangleListAsRegion.op(rectRegion, SkRegion::kIntersect_Op);
    180         }
    181     }
    182     return rectangleListAsRegion;
    183 }
    184 
    185 /*
    186  * ClipArea
    187  */
    188 
    189 ClipArea::ClipArea()
    190         : mMode(kModeRectangle) {
    191 }
    192 
    193 /*
    194  * Interface
    195  */
    196 
    197 void ClipArea::setViewportDimensions(int width, int height) {
    198     mViewportBounds.set(0, 0, width, height);
    199     mClipRect = mViewportBounds;
    200 }
    201 
    202 void ClipArea::setEmpty() {
    203     mMode = kModeRectangle;
    204     mClipRect.setEmpty();
    205     mClipRegion.setEmpty();
    206     mRectangleList.setEmpty();
    207 }
    208 
    209 void ClipArea::setClip(float left, float top, float right, float bottom) {
    210     mMode = kModeRectangle;
    211     mClipRect.set(left, top, right, bottom);
    212     mClipRegion.setEmpty();
    213 }
    214 
    215 bool ClipArea::clipRectWithTransform(float left, float top, float right,
    216         float bottom, const mat4* transform, SkRegion::Op op) {
    217     Rect r(left, top, right, bottom);
    218     return clipRectWithTransform(r, transform, op);
    219 }
    220 
    221 bool ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform,
    222         SkRegion::Op op) {
    223     switch (mMode) {
    224     case kModeRectangle:
    225         return rectangleModeClipRectWithTransform(r, transform, op);
    226     case kModeRectangleList:
    227         return rectangleListModeClipRectWithTransform(r, transform, op);
    228     case kModeRegion:
    229         return regionModeClipRectWithTransform(r, transform, op);
    230     }
    231     return false;
    232 }
    233 
    234 bool ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
    235     enterRegionMode();
    236     mClipRegion.op(region, op);
    237     onClipRegionUpdated();
    238     return true;
    239 }
    240 
    241 bool ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform,
    242         SkRegion::Op op) {
    243     SkMatrix skTransform;
    244     transform->copyTo(skTransform);
    245     SkPath transformed;
    246     path.transform(skTransform, &transformed);
    247     SkRegion region;
    248     regionFromPath(transformed, region);
    249     return clipRegion(region, op);
    250 }
    251 
    252 /*
    253  * Rectangle mode
    254  */
    255 
    256 void ClipArea::enterRectangleMode() {
    257     // Entering rectangle mode discards any
    258     // existing clipping information from the other modes.
    259     // The only way this occurs is by a clip setting operation.
    260     mMode = kModeRectangle;
    261 }
    262 
    263 bool ClipArea::rectangleModeClipRectWithTransform(const Rect& r,
    264         const mat4* transform, SkRegion::Op op) {
    265 
    266     if (op == SkRegion::kReplace_Op && transform->rectToRect()) {
    267         mClipRect = r;
    268         transform->mapRect(mClipRect);
    269         return true;
    270     } else if (op != SkRegion::kIntersect_Op) {
    271         enterRegionMode();
    272         return regionModeClipRectWithTransform(r, transform, op);
    273     }
    274 
    275     if (transform->rectToRect()) {
    276         Rect transformed(r);
    277         transform->mapRect(transformed);
    278         bool hasIntersection = mClipRect.intersect(transformed);
    279         if (!hasIntersection) {
    280             mClipRect.setEmpty();
    281         }
    282         return true;
    283     }
    284 
    285     enterRectangleListMode();
    286     return rectangleListModeClipRectWithTransform(r, transform, op);
    287 }
    288 
    289 bool ClipArea::rectangleModeClipRectWithTransform(float left, float top,
    290         float right, float bottom, const mat4* transform, SkRegion::Op op) {
    291     Rect r(left, top, right, bottom);
    292     bool result = rectangleModeClipRectWithTransform(r, transform, op);
    293     mClipRect = mRectangleList.calculateBounds();
    294     return result;
    295 }
    296 
    297 /*
    298  * RectangleList mode implementation
    299  */
    300 
    301 void ClipArea::enterRectangleListMode() {
    302     // Is is only legal to enter rectangle list mode from
    303     // rectangle mode, since rectangle list mode cannot represent
    304     // all clip areas that can be represented by a region.
    305     ALOG_ASSERT(mMode == kModeRectangle);
    306     mMode = kModeRectangleList;
    307     mRectangleList.set(mClipRect, Matrix4::identity());
    308 }
    309 
    310 bool ClipArea::rectangleListModeClipRectWithTransform(const Rect& r,
    311         const mat4* transform, SkRegion::Op op) {
    312     if (op != SkRegion::kIntersect_Op
    313             || !mRectangleList.intersectWith(r, *transform)) {
    314         enterRegionMode();
    315         return regionModeClipRectWithTransform(r, transform, op);
    316     }
    317     return true;
    318 }
    319 
    320 bool ClipArea::rectangleListModeClipRectWithTransform(float left, float top,
    321         float right, float bottom, const mat4* transform, SkRegion::Op op) {
    322     Rect r(left, top, right, bottom);
    323     return rectangleListModeClipRectWithTransform(r, transform, op);
    324 }
    325 
    326 /*
    327  * Region mode implementation
    328  */
    329 
    330 void ClipArea::enterRegionMode() {
    331     Mode oldMode = mMode;
    332     mMode = kModeRegion;
    333     if (oldMode != kModeRegion) {
    334         if (oldMode == kModeRectangle) {
    335             mClipRegion.setRect(mClipRect.left, mClipRect.top,
    336                     mClipRect.right, mClipRect.bottom);
    337         } else {
    338             mClipRegion = mRectangleList.convertToRegion(createViewportRegion());
    339             onClipRegionUpdated();
    340         }
    341     }
    342 }
    343 
    344 bool ClipArea::regionModeClipRectWithTransform(const Rect& r,
    345         const mat4* transform, SkRegion::Op op) {
    346     SkPath transformedRect = pathFromTransformedRectangle(r, *transform);
    347     SkRegion transformedRectRegion;
    348     regionFromPath(transformedRect, transformedRectRegion);
    349     mClipRegion.op(transformedRectRegion, op);
    350     onClipRegionUpdated();
    351     return true;
    352 }
    353 
    354 bool ClipArea::regionModeClipRectWithTransform(float left, float top,
    355         float right, float bottom, const mat4* transform, SkRegion::Op op) {
    356     return regionModeClipRectWithTransform(Rect(left, top, right, bottom),
    357             transform, op);
    358 }
    359 
    360 void ClipArea::onClipRegionUpdated() {
    361     if (!mClipRegion.isEmpty()) {
    362         mClipRect.set(mClipRegion.getBounds());
    363 
    364         if (mClipRegion.isRect()) {
    365             mClipRegion.setEmpty();
    366             enterRectangleMode();
    367         }
    368     } else {
    369         mClipRect.setEmpty();
    370     }
    371 }
    372 
    373 } /* namespace uirenderer */
    374 } /* namespace android */
    375