ClipArea.cpp revision 487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8
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 23namespace android { 24namespace uirenderer { 25 26static 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 34static 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 42Rect 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 57TransformedRectangle::TransformedRectangle() { 58} 59 60TransformedRectangle::TransformedRectangle(const Rect& bounds, 61 const Matrix4& transform) 62 : mBounds(bounds) 63 , mTransform(transform) { 64} 65 66bool TransformedRectangle::canSimplyIntersectWith( 67 const TransformedRectangle& other) const { 68 69 return mTransform == other.mTransform; 70} 71 72bool TransformedRectangle::intersectWith(const TransformedRectangle& other) { 73 Rect translatedBounds(other.mBounds); 74 return intersect(mBounds, translatedBounds); 75} 76 77bool TransformedRectangle::isEmpty() const { 78 return mBounds.isEmpty(); 79} 80 81/* 82 * RectangleList 83 */ 84 85RectangleList::RectangleList() 86 : mTransformedRectanglesCount(0) { 87} 88 89bool 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 102int RectangleList::getTransformedRectanglesCount() const { 103 return mTransformedRectanglesCount; 104} 105 106const TransformedRectangle& RectangleList::getTransformedRectangle(int i) const { 107 return mTransformedRectangles[i]; 108} 109 110void RectangleList::setEmpty() { 111 mTransformedRectanglesCount = 0; 112} 113 114void RectangleList::set(const Rect& bounds, const Matrix4& transform) { 115 mTransformedRectanglesCount = 1; 116 mTransformedRectangles[0] = TransformedRectangle(bounds, transform); 117} 118 119bool 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 144Rect 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 157static 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 168SkRegion 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 189ClipArea::ClipArea() 190 : mMode(kModeRectangle) { 191} 192 193/* 194 * Interface 195 */ 196 197void ClipArea::setViewportDimensions(int width, int height) { 198 mViewportBounds.set(0, 0, width, height); 199 mClipRect = mViewportBounds; 200} 201 202void ClipArea::setEmpty() { 203 mMode = kModeRectangle; 204 mClipRect.setEmpty(); 205 mClipRegion.setEmpty(); 206 mRectangleList.setEmpty(); 207} 208 209void 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 215bool 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 221bool 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 234bool ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) { 235 enterRegionMode(); 236 mClipRegion.op(region, op); 237 setClipRectToRegionBounds(); 238 return true; 239} 240 241bool 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 256void 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 263bool ClipArea::rectangleModeClipRectWithTransform(const Rect& r, 264 const mat4* transform, SkRegion::Op op) { 265 266 if (op != SkRegion::kIntersect_Op) { 267 enterRegionMode(); 268 return regionModeClipRectWithTransform(r, transform, op); 269 } 270 271 if (transform->rectToRect()) { 272 Rect transformed(r); 273 transform->mapRect(transformed); 274 bool hasIntersection = mClipRect.intersect(transformed); 275 if (!hasIntersection) { 276 mClipRect.setEmpty(); 277 } 278 return true; 279 } 280 281 enterRectangleListMode(); 282 return rectangleListModeClipRectWithTransform(r, transform, op); 283} 284 285bool ClipArea::rectangleModeClipRectWithTransform(float left, float top, 286 float right, float bottom, const mat4* transform, SkRegion::Op op) { 287 Rect r(left, top, right, bottom); 288 bool result = rectangleModeClipRectWithTransform(r, transform, op); 289 mClipRect = mRectangleList.calculateBounds(); 290 return result; 291} 292 293/* 294 * RectangleList mode implementation 295 */ 296 297void ClipArea::enterRectangleListMode() { 298 // Is is only legal to enter rectangle list mode from 299 // rectangle mode, since rectangle list mode cannot represent 300 // all clip areas that can be represented by a region. 301 ALOG_ASSERT(mMode == kModeRectangle); 302 mMode = kModeRectangleList; 303 mRectangleList.set(mClipRect, Matrix4::identity()); 304} 305 306bool ClipArea::rectangleListModeClipRectWithTransform(const Rect& r, 307 const mat4* transform, SkRegion::Op op) { 308 if (op != SkRegion::kIntersect_Op 309 || !mRectangleList.intersectWith(r, *transform)) { 310 enterRegionMode(); 311 return regionModeClipRectWithTransform(r, transform, op); 312 } 313 return true; 314} 315 316bool ClipArea::rectangleListModeClipRectWithTransform(float left, float top, 317 float right, float bottom, const mat4* transform, SkRegion::Op op) { 318 Rect r(left, top, right, bottom); 319 return rectangleListModeClipRectWithTransform(r, transform, op); 320} 321 322/* 323 * Region mode implementation 324 */ 325 326void ClipArea::enterRegionMode() { 327 if (mMode != kModeRegion) { 328 if (mMode == kModeRectangle) { 329 mClipRegion.setRect(mClipRect.left, mClipRect.top, 330 mClipRect.right, mClipRect.bottom); 331 } else { 332 mClipRegion = mRectangleList.convertToRegion(createViewportRegion()); 333 setClipRectToRegionBounds(); 334 } 335 mMode = kModeRegion; 336 } 337} 338 339bool ClipArea::regionModeClipRectWithTransform(const Rect& r, 340 const mat4* transform, SkRegion::Op op) { 341 SkPath transformedRect = pathFromTransformedRectangle(r, *transform); 342 SkRegion transformedRectRegion; 343 regionFromPath(transformedRect, transformedRectRegion); 344 mClipRegion.op(transformedRectRegion, op); 345 setClipRectToRegionBounds(); 346 return true; 347} 348 349bool ClipArea::regionModeClipRectWithTransform(float left, float top, 350 float right, float bottom, const mat4* transform, SkRegion::Op op) { 351 return regionModeClipRectWithTransform(Rect(left, top, right, bottom), 352 transform, op); 353} 354 355void ClipArea::setClipRectToRegionBounds() { 356 if (!mClipRegion.isEmpty()) { 357 mClipRect.set(mClipRegion.getBounds()); 358 359 if (mClipRegion.isRect()) { 360 mClipRegion.setEmpty(); 361 } 362 } else { 363 mClipRect.setEmpty(); 364 } 365} 366 367} /* namespace uirenderer */ 368} /* namespace android */ 369