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