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 onClipRegionUpdated(); 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::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 289bool 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 301void 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 310bool 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 320bool 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 330void 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 344bool 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 354bool 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 360void 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