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