ClipAreaTests.cpp revision 69aeabe6184bbbe6e731656205ffbef78b7b0f3f
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 27namespace android { 28namespace uirenderer { 29 30static Rect kViewportBounds(2048, 2048); 31 32static ClipArea createClipArea() { 33 ClipArea area; 34 area.setViewportDimensions(kViewportBounds.getWidth(), kViewportBounds.getHeight()); 35 return area; 36} 37 38TEST(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 60TEST(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 88TEST(ClipArea, basics) { 89 ClipArea area(createClipArea()); 90 EXPECT_FALSE(area.isEmpty()); 91} 92 93TEST(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 113TEST(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 122TEST(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 auto clipRect = reinterpret_cast<const ClipRect*>(serializedClip); 136 EXPECT_EQ(Rect(200, 200), clipRect->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 auto clipRectList = reinterpret_cast<const ClipRectList*>(serializedClip); 150 EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount()); 151 EXPECT_EQ(Rect(37, 54, 145, 163), clipRectList->rect); 152 EXPECT_EQ(serializedClip, area.serializeClip(allocator)) 153 << "Requery of clip on unmodified ClipArea must return same pointer."; 154 } 155 156 // region 157 SkPath circlePath; 158 circlePath.addCircle(100, 100, 100); 159 area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op); 160 { 161 auto serializedClip = area.serializeClip(allocator); 162 ASSERT_NE(nullptr, serializedClip); 163 ASSERT_EQ(ClipMode::Region, serializedClip->mode); 164 auto clipRegion = reinterpret_cast<const ClipRegion*>(serializedClip); 165 EXPECT_EQ(SkIRect::MakeWH(200, 200), clipRegion->region.getBounds()) 166 << "Clip region should be 200x200"; 167 EXPECT_EQ(Rect(200, 200), clipRegion->rect); 168 EXPECT_EQ(serializedClip, area.serializeClip(allocator)) 169 << "Requery of clip on unmodified ClipArea must return same pointer."; 170 } 171} 172 173TEST(ClipArea, serializeIntersectedClip) { 174 ClipArea area(createClipArea()); 175 LinearAllocator allocator; 176 177 // simple state; 178 EXPECT_EQ(nullptr, area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity())); 179 area.setClip(0, 0, 200, 200); 180 { 181 auto origRectClip = area.serializeClip(allocator); 182 ASSERT_NE(nullptr, origRectClip); 183 EXPECT_EQ(origRectClip, area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity())); 184 } 185 186 // rect 187 { 188 ClipRect recordedClip(Rect(100, 100)); 189 Matrix4 translateScale; 190 translateScale.loadTranslate(100, 100, 0); 191 translateScale.scale(2, 3, 1); 192 auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale); 193 ASSERT_NE(nullptr, resolvedClip); 194 ASSERT_EQ(ClipMode::Rectangle, resolvedClip->mode); 195 EXPECT_EQ(Rect(100, 100, 200, 200), 196 reinterpret_cast<const ClipRect*>(resolvedClip)->rect); 197 198 EXPECT_EQ(resolvedClip, area.serializeIntersectedClip(allocator, &recordedClip, translateScale)) 199 << "Must return previous serialization, since input is same"; 200 201 ClipRect recordedClip2(Rect(100, 100)); 202 EXPECT_NE(resolvedClip, area.serializeIntersectedClip(allocator, &recordedClip2, translateScale)) 203 << "Shouldn't return previous serialization, since matrix location is different"; 204 } 205 206 // rect list 207 Matrix4 rotate; 208 rotate.loadRotate(2.0f); 209 area.clipRectWithTransform(Rect(200, 200), &rotate, SkRegion::kIntersect_Op); 210 { 211 ClipRect recordedClip(Rect(100, 100)); 212 auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, Matrix4::identity()); 213 ASSERT_NE(nullptr, resolvedClip); 214 ASSERT_EQ(ClipMode::RectangleList, resolvedClip->mode); 215 auto clipRectList = reinterpret_cast<const ClipRectList*>(resolvedClip); 216 EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount()); 217 } 218 219 // region 220 SkPath circlePath; 221 circlePath.addCircle(100, 100, 100); 222 area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op); 223 { 224 SkPath ovalPath; 225 ovalPath.addOval(SkRect::MakeLTRB(50, 0, 150, 200)); 226 227 ClipRegion recordedClip; 228 recordedClip.region.setPath(ovalPath, SkRegion(SkIRect::MakeWH(200, 200))); 229 recordedClip.rect = Rect(200, 200); 230 231 Matrix4 translate10x20; 232 translate10x20.loadTranslate(10, 20, 0); 233 auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, 234 translate10x20); // Note: only translate for now, others not handled correctly 235 ASSERT_NE(nullptr, resolvedClip); 236 ASSERT_EQ(ClipMode::Region, resolvedClip->mode); 237 auto clipRegion = reinterpret_cast<const ClipRegion*>(resolvedClip); 238 EXPECT_EQ(SkIRect::MakeLTRB(60, 20, 160, 200), clipRegion->region.getBounds()); 239 } 240} 241 242TEST(ClipArea, serializeIntersectedClip_snap) { 243 ClipArea area(createClipArea()); 244 area.setClip(100.2, 100.4, 500.6, 500.8); 245 LinearAllocator allocator; 246 247 { 248 // no recorded clip case 249 auto resolvedClip = area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity()); 250 EXPECT_EQ(Rect(100, 100, 501, 501), resolvedClip->rect); 251 } 252 { 253 // recorded clip case 254 ClipRect recordedClip(Rect(100.12, 100.74)); 255 Matrix4 translateScale; 256 translateScale.loadTranslate(100, 100, 0); 257 translateScale.scale(2, 3, 1); // recorded clip will have non-int coords, even after transform 258 auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale); 259 ASSERT_NE(nullptr, resolvedClip); 260 EXPECT_EQ(ClipMode::Rectangle, resolvedClip->mode); 261 EXPECT_EQ(Rect(100, 100, 300, 402), resolvedClip->rect); 262 } 263} 264 265} // namespace uirenderer 266} // namespace android 267