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 17 #include <gtest/gtest.h> 18 #include <SkPath.h> 19 #include <SkRegion.h> 20 21 #include "ClipArea.h" 22 23 #include "Matrix.h" 24 #include "Rect.h" 25 #include "utils/LinearAllocator.h" 26 27 namespace android { 28 namespace uirenderer { 29 30 static Rect kViewportBounds(2048, 2048); 31 32 static ClipArea createClipArea() { 33 ClipArea area; 34 area.setViewportDimensions(kViewportBounds.getWidth(), kViewportBounds.getHeight()); 35 return area; 36 } 37 38 TEST(TransformedRectangle, basics) { 39 Rect r(0, 0, 100, 100); 40 Matrix4 minus90; 41 minus90.loadRotate(-90); 42 minus90.mapRect(r); 43 Rect r2(20, 40, 120, 60); 44 45 Matrix4 m90; 46 m90.loadRotate(90); 47 TransformedRectangle tr(r, m90); 48 EXPECT_TRUE(tr.canSimplyIntersectWith(tr)); 49 50 Matrix4 m0; 51 TransformedRectangle tr0(r2, m0); 52 EXPECT_FALSE(tr.canSimplyIntersectWith(tr0)); 53 54 Matrix4 m45; 55 m45.loadRotate(45); 56 TransformedRectangle tr2(r, m45); 57 EXPECT_FALSE(tr2.canSimplyIntersectWith(tr)); 58 } 59 60 TEST(RectangleList, basics) { 61 RectangleList list; 62 EXPECT_TRUE(list.isEmpty()); 63 64 Rect r(0, 0, 100, 100); 65 Matrix4 m45; 66 m45.loadRotate(45); 67 list.set(r, m45); 68 EXPECT_FALSE(list.isEmpty()); 69 70 Rect r2(20, 20, 200, 200); 71 list.intersectWith(r2, m45); 72 EXPECT_FALSE(list.isEmpty()); 73 EXPECT_EQ(1, list.getTransformedRectanglesCount()); 74 75 Rect r3(20, 20, 200, 200); 76 Matrix4 m30; 77 m30.loadRotate(30); 78 list.intersectWith(r2, m30); 79 EXPECT_FALSE(list.isEmpty()); 80 EXPECT_EQ(2, list.getTransformedRectanglesCount()); 81 82 SkRegion clip; 83 clip.setRect(0, 0, 2000, 2000); 84 SkRegion rgn(list.convertToRegion(clip)); 85 EXPECT_FALSE(rgn.isEmpty()); 86 } 87 88 TEST(ClipArea, basics) { 89 ClipArea area(createClipArea()); 90 EXPECT_FALSE(area.isEmpty()); 91 } 92 93 TEST(ClipArea, paths) { 94 ClipArea area(createClipArea()); 95 SkPath path; 96 SkScalar r = 100; 97 path.addCircle(r, r, r); 98 area.clipPathWithTransform(path, &Matrix4::identity(), SkRegion::kIntersect_Op); 99 EXPECT_FALSE(area.isEmpty()); 100 EXPECT_FALSE(area.isSimple()); 101 EXPECT_FALSE(area.isRectangleList()); 102 103 Rect clipRect(area.getClipRect()); 104 Rect expected(0, 0, r * 2, r * 2); 105 EXPECT_EQ(expected, clipRect); 106 SkRegion clipRegion(area.getClipRegion()); 107 auto skRect(clipRegion.getBounds()); 108 Rect regionBounds; 109 regionBounds.set(skRect); 110 EXPECT_EQ(expected, regionBounds); 111 } 112 113 TEST(ClipArea, replaceNegative) { 114 ClipArea area(createClipArea()); 115 area.setClip(0, 0, 100, 100); 116 117 Rect expected(-50, -50, 50, 50); 118 area.clipRectWithTransform(expected, &Matrix4::identity(), SkRegion::kReplace_Op); 119 EXPECT_EQ(expected, area.getClipRect()); 120 } 121 122 TEST(ClipArea, serializeClip) { 123 ClipArea area(createClipArea()); 124 LinearAllocator allocator; 125 126 // unset clip 127 EXPECT_EQ(nullptr, area.serializeClip(allocator)); 128 129 // rect clip 130 area.setClip(0, 0, 200, 200); 131 { 132 auto serializedClip = area.serializeClip(allocator); 133 ASSERT_NE(nullptr, serializedClip); 134 ASSERT_EQ(ClipMode::Rectangle, serializedClip->mode); 135 ASSERT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot"; 136 EXPECT_EQ(Rect(200, 200), serializedClip->rect); 137 EXPECT_EQ(serializedClip, area.serializeClip(allocator)) 138 << "Requery of clip on unmodified ClipArea must return same pointer."; 139 } 140 141 // rect list 142 Matrix4 rotate; 143 rotate.loadRotate(5.0f); 144 area.clipRectWithTransform(Rect(50, 50, 150, 150), &rotate, SkRegion::kIntersect_Op); 145 { 146 auto serializedClip = area.serializeClip(allocator); 147 ASSERT_NE(nullptr, serializedClip); 148 ASSERT_EQ(ClipMode::RectangleList, serializedClip->mode); 149 ASSERT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot"; 150 auto clipRectList = reinterpret_cast<const ClipRectList*>(serializedClip); 151 EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount()); 152 EXPECT_EQ(Rect(37, 54, 145, 163), clipRectList->rect); 153 EXPECT_EQ(serializedClip, area.serializeClip(allocator)) 154 << "Requery of clip on unmodified ClipArea must return same pointer."; 155 } 156 157 // region 158 SkPath circlePath; 159 circlePath.addCircle(100, 100, 100); 160 area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op); 161 { 162 auto serializedClip = area.serializeClip(allocator); 163 ASSERT_NE(nullptr, serializedClip); 164 ASSERT_EQ(ClipMode::Region, serializedClip->mode); 165 ASSERT_TRUE(serializedClip->intersectWithRoot) << "Replace op, so expect intersectWithRoot"; 166 auto clipRegion = reinterpret_cast<const ClipRegion*>(serializedClip); 167 EXPECT_EQ(SkIRect::MakeWH(200, 200), clipRegion->region.getBounds()) 168 << "Clip region should be 200x200"; 169 EXPECT_EQ(Rect(200, 200), clipRegion->rect); 170 EXPECT_EQ(serializedClip, area.serializeClip(allocator)) 171 << "Requery of clip on unmodified ClipArea must return same pointer."; 172 } 173 } 174 175 TEST(ClipArea, serializeClip_pathIntersectWithRoot) { 176 ClipArea area(createClipArea()); 177 LinearAllocator allocator; 178 SkPath circlePath; 179 circlePath.addCircle(100, 100, 100); 180 area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kIntersect_Op); 181 182 auto serializedClip = area.serializeClip(allocator); 183 ASSERT_NE(nullptr, serializedClip); 184 EXPECT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot"; 185 } 186 187 TEST(ClipArea, serializeIntersectedClip) { 188 ClipArea area(createClipArea()); 189 LinearAllocator allocator; 190 191 // simple state; 192 EXPECT_EQ(nullptr, area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity())); 193 area.setClip(0, 0, 200, 200); 194 { 195 auto origRectClip = area.serializeClip(allocator); 196 ASSERT_NE(nullptr, origRectClip); 197 EXPECT_EQ(origRectClip, area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity())); 198 } 199 200 // rect 201 { 202 ClipRect recordedClip(Rect(100, 100)); 203 Matrix4 translateScale; 204 translateScale.loadTranslate(100, 100, 0); 205 translateScale.scale(2, 3, 1); 206 auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale); 207 ASSERT_NE(nullptr, resolvedClip); 208 ASSERT_EQ(ClipMode::Rectangle, resolvedClip->mode); 209 EXPECT_EQ(Rect(100, 100, 200, 200), resolvedClip->rect); 210 211 EXPECT_EQ(resolvedClip, area.serializeIntersectedClip(allocator, &recordedClip, translateScale)) 212 << "Must return previous serialization, since input is same"; 213 214 ClipRect recordedClip2(Rect(100, 100)); 215 EXPECT_NE(resolvedClip, area.serializeIntersectedClip(allocator, &recordedClip2, translateScale)) 216 << "Shouldn't return previous serialization, since matrix location is different"; 217 } 218 219 // rect list 220 Matrix4 rotate; 221 rotate.loadRotate(2.0f); 222 area.clipRectWithTransform(Rect(200, 200), &rotate, SkRegion::kIntersect_Op); 223 { 224 ClipRect recordedClip(Rect(100, 100)); 225 auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, Matrix4::identity()); 226 ASSERT_NE(nullptr, resolvedClip); 227 ASSERT_EQ(ClipMode::RectangleList, resolvedClip->mode); 228 auto clipRectList = reinterpret_cast<const ClipRectList*>(resolvedClip); 229 EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount()); 230 } 231 232 // region 233 SkPath circlePath; 234 circlePath.addCircle(100, 100, 100); 235 area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op); 236 { 237 SkPath ovalPath; 238 ovalPath.addOval(SkRect::MakeLTRB(50, 0, 150, 200)); 239 240 ClipRegion recordedClip; 241 recordedClip.region.setPath(ovalPath, SkRegion(SkIRect::MakeWH(200, 200))); 242 recordedClip.rect = Rect(200, 200); 243 244 Matrix4 translate10x20; 245 translate10x20.loadTranslate(10, 20, 0); 246 auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, 247 translate10x20); // Note: only translate for now, others not handled correctly 248 ASSERT_NE(nullptr, resolvedClip); 249 ASSERT_EQ(ClipMode::Region, resolvedClip->mode); 250 auto clipRegion = reinterpret_cast<const ClipRegion*>(resolvedClip); 251 EXPECT_EQ(SkIRect::MakeLTRB(60, 20, 160, 200), clipRegion->region.getBounds()); 252 } 253 } 254 255 TEST(ClipArea, serializeIntersectedClip_snap) { 256 ClipArea area(createClipArea()); 257 area.setClip(100.2, 100.4, 500.6, 500.8); 258 LinearAllocator allocator; 259 260 { 261 // no recorded clip case 262 auto resolvedClip = area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity()); 263 EXPECT_EQ(Rect(100, 100, 501, 501), resolvedClip->rect); 264 } 265 { 266 // recorded clip case 267 ClipRect recordedClip(Rect(100.12, 100.74)); 268 Matrix4 translateScale; 269 translateScale.loadTranslate(100, 100, 0); 270 translateScale.scale(2, 3, 1); // recorded clip will have non-int coords, even after transform 271 auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale); 272 ASSERT_NE(nullptr, resolvedClip); 273 EXPECT_EQ(ClipMode::Rectangle, resolvedClip->mode); 274 EXPECT_EQ(Rect(100, 100, 300, 402), resolvedClip->rect); 275 } 276 } 277 278 TEST(ClipArea, serializeIntersectedClip_scale) { 279 ClipArea area(createClipArea()); 280 area.setClip(0, 0, 400, 400); 281 LinearAllocator allocator; 282 283 SkPath circlePath; 284 circlePath.addCircle(50, 50, 50); 285 286 ClipRegion recordedClip; 287 recordedClip.region.setPath(circlePath, SkRegion(SkIRect::MakeWH(100, 100))); 288 recordedClip.rect = Rect(100, 100); 289 290 Matrix4 translateScale; 291 translateScale.loadTranslate(100, 100, 0); 292 translateScale.scale(2, 2, 1); 293 auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale); 294 295 ASSERT_NE(nullptr, resolvedClip); 296 EXPECT_EQ(ClipMode::Region, resolvedClip->mode); 297 EXPECT_EQ(Rect(100, 100, 300, 300), resolvedClip->rect); 298 auto clipRegion = reinterpret_cast<const ClipRegion*>(resolvedClip); 299 EXPECT_EQ(SkIRect::MakeLTRB(100, 100, 300, 300), clipRegion->region.getBounds()); 300 } 301 302 TEST(ClipArea, applyTransformToRegion_identity) { 303 SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4)); 304 ClipArea::applyTransformToRegion(Matrix4::identity(), ®ion); 305 EXPECT_TRUE(region.isRect()); 306 EXPECT_EQ(SkIRect::MakeLTRB(1, 2, 3, 4), region.getBounds()); 307 } 308 309 TEST(ClipArea, applyTransformToRegion_translate) { 310 SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4)); 311 Matrix4 transform; 312 transform.loadTranslate(10, 20, 0); 313 ClipArea::applyTransformToRegion(transform, ®ion); 314 EXPECT_TRUE(region.isRect()); 315 EXPECT_EQ(SkIRect::MakeLTRB(11, 22, 13, 24), region.getBounds()); 316 } 317 318 TEST(ClipArea, applyTransformToRegion_scale) { 319 SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4)); 320 Matrix4 transform; 321 transform.loadScale(2, 3, 1); 322 ClipArea::applyTransformToRegion(transform, ®ion); 323 EXPECT_TRUE(region.isRect()); 324 EXPECT_EQ(SkIRect::MakeLTRB(2, 6, 6, 12), region.getBounds()); 325 } 326 327 TEST(ClipArea, applyTransformToRegion_translateScale) { 328 SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4)); 329 Matrix4 transform; 330 transform.translate(10, 20); 331 transform.scale(2, 3, 1); 332 ClipArea::applyTransformToRegion(transform, ®ion); 333 EXPECT_TRUE(region.isRect()); 334 EXPECT_EQ(SkIRect::MakeLTRB(12, 26, 16, 32), region.getBounds()); 335 } 336 337 TEST(ClipArea, applyTransformToRegion_rotate90) { 338 SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4)); 339 Matrix4 transform; 340 transform.loadRotate(90); 341 ClipArea::applyTransformToRegion(transform, ®ion); 342 EXPECT_TRUE(region.isRect()); 343 EXPECT_EQ(SkIRect::MakeLTRB(-4, 1, -2, 3), region.getBounds()); 344 } 345 346 } // namespace uirenderer 347 } // namespace android 348