• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
19 #include <BakedOpState.h>
20 #include <ClipArea.h>
21 #include <RecordedOp.h>
22 #include <tests/common/TestUtils.h>
23 
24 namespace android {
25 namespace uirenderer {
26 
TEST(ResolvedRenderState,construct)27 TEST(ResolvedRenderState, construct) {
28     LinearAllocator allocator;
29     Matrix4 translate10x20;
30     translate10x20.loadTranslate(10, 20, 0);
31 
32     SkPaint paint;
33     ClipRect clip(Rect(100, 200));
34     RectOp recordedOp(Rect(30, 40, 100, 200), translate10x20, &clip, &paint);
35     {
36         // recorded with transform, no parent transform
37         auto parentSnapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
38         ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false);
39         EXPECT_MATRIX_APPROX_EQ(state.transform, translate10x20);
40         EXPECT_EQ(Rect(100, 200), state.clipRect());
41         EXPECT_EQ(Rect(40, 60, 100, 200), state.clippedBounds);  // translated and also clipped
42         EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags);
43     }
44     {
45         // recorded with transform and parent transform
46         auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
47         ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false);
48 
49         Matrix4 expectedTranslate;
50         expectedTranslate.loadTranslate(20, 40, 0);
51         EXPECT_MATRIX_APPROX_EQ(expectedTranslate, state.transform);
52 
53         // intersection of parent & transformed child clip
54         EXPECT_EQ(Rect(10, 20, 100, 200), state.clipRect());
55 
56         // translated and also clipped
57         EXPECT_EQ(Rect(50, 80, 100, 200), state.clippedBounds);
58         EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags);
59     }
60 }
61 
TEST(ResolvedRenderState,computeLocalSpaceClip)62 TEST(ResolvedRenderState, computeLocalSpaceClip) {
63     LinearAllocator allocator;
64     Matrix4 translate10x20;
65     translate10x20.loadTranslate(10, 20, 0);
66 
67     SkPaint paint;
68     ClipRect clip(Rect(100, 200));
69     RectOp recordedOp(Rect(1000, 1000), translate10x20, &clip, &paint);
70     {
71         // recorded with transform, no parent transform
72         auto parentSnapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
73         ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false);
74         EXPECT_EQ(Rect(-10, -20, 90, 180), state.computeLocalSpaceClip())
75                 << "Local clip rect should be 100x200, offset by -10,-20";
76     }
77     {
78         // recorded with transform + parent transform
79         auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
80         ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false);
81         EXPECT_EQ(Rect(-10, -20, 80, 160), state.computeLocalSpaceClip())
82                 << "Local clip rect should be 90x190, offset by -10,-20";
83     }
84 }
85 
86 const float HAIRLINE = 0.0f;
87 
88 // Note: bounds will be conservative, but not precise for non-hairline
89 // - use approx bounds checks for these
90 const float SEMI_HAIRLINE = 0.3f;
91 
92 struct StrokeTestCase {
93     float scale;
94     float strokeWidth;
95     const std::function<void(const ResolvedRenderState&)> validator;
96 };
97 
98 const static StrokeTestCase sStrokeTestCases[] = {
99         {1, HAIRLINE,
__anon3ac3d9670102() 100          [](const ResolvedRenderState& state) {
101              EXPECT_EQ(Rect(49.5f, 49.5f, 150.5f, 150.5f), state.clippedBounds);
102          }},
103         {1, SEMI_HAIRLINE,
__anon3ac3d9670202() 104          [](const ResolvedRenderState& state) {
105              EXPECT_TRUE(state.clippedBounds.contains(49.5f, 49.5f, 150.5f, 150.5f));
106              EXPECT_TRUE(Rect(49, 49, 151, 151).contains(state.clippedBounds));
107          }},
108         {1, 20,
__anon3ac3d9670302() 109          [](const ResolvedRenderState& state) {
110              EXPECT_EQ(Rect(40, 40, 160, 160), state.clippedBounds);
111          }},
112 
113         // 3x3 scale:
114         {3, HAIRLINE,
__anon3ac3d9670402() 115          [](const ResolvedRenderState& state) {
116              EXPECT_EQ(Rect(149.5f, 149.5f, 200, 200), state.clippedBounds);
117              EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags);
118          }},
119         {3, SEMI_HAIRLINE,
__anon3ac3d9670502() 120          [](const ResolvedRenderState& state) {
121              EXPECT_TRUE(state.clippedBounds.contains(149.5f, 149.5f, 200, 200));
122              EXPECT_TRUE(Rect(149, 149, 200, 200).contains(state.clippedBounds));
123          }},
124         {3, 20,
__anon3ac3d9670602() 125          [](const ResolvedRenderState& state) {
126              EXPECT_TRUE(state.clippedBounds.contains(120, 120, 200, 200));
127              EXPECT_TRUE(Rect(119, 119, 200, 200).contains(state.clippedBounds));
128          }},
129 
130         // 0.5f x 0.5f scale
131         {0.5f, HAIRLINE,
__anon3ac3d9670702() 132          [](const ResolvedRenderState& state) {
133              EXPECT_EQ(Rect(24.5f, 24.5f, 75.5f, 75.5f), state.clippedBounds);
134          }},
135         {0.5f, SEMI_HAIRLINE,
__anon3ac3d9670802() 136          [](const ResolvedRenderState& state) {
137              EXPECT_TRUE(state.clippedBounds.contains(24.5f, 24.5f, 75.5f, 75.5f));
138              EXPECT_TRUE(Rect(24, 24, 76, 76).contains(state.clippedBounds));
139          }},
__anon3ac3d9670902() 140         {0.5f, 20, [](const ResolvedRenderState& state) {
141              EXPECT_TRUE(state.clippedBounds.contains(19.5f, 19.5f, 80.5f, 80.5f));
142              EXPECT_TRUE(Rect(19, 19, 81, 81).contains(state.clippedBounds));
143          }}};
144 
TEST(ResolvedRenderState,construct_expandForStroke)145 TEST(ResolvedRenderState, construct_expandForStroke) {
146     LinearAllocator allocator;
147     // Loop over table of test cases and verify different combinations of stroke width and transform
148     for (auto&& testCase : sStrokeTestCases) {
149         SkPaint strokedPaint;
150         strokedPaint.setAntiAlias(true);
151         strokedPaint.setStyle(SkPaint::kStroke_Style);
152         strokedPaint.setStrokeWidth(testCase.strokeWidth);
153 
154         ClipRect clip(Rect(200, 200));
155         RectOp recordedOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &strokedPaint);
156 
157         Matrix4 snapshotMatrix;
158         snapshotMatrix.loadScale(testCase.scale, testCase.scale, 1);
159         auto parentSnapshot = TestUtils::makeSnapshot(snapshotMatrix, Rect(200, 200));
160 
161         ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, true, false);
162         testCase.validator(state);
163     }
164 }
165 
TEST(BakedOpState,tryConstruct)166 TEST(BakedOpState, tryConstruct) {
167     Matrix4 translate100x0;
168     translate100x0.loadTranslate(100, 0, 0);
169 
170     SkPaint paint;
171     ClipRect clip(Rect(100, 200));
172 
173     LinearAllocator allocator;
174     RectOp successOp(Rect(30, 40, 100, 200), Matrix4::identity(), &clip, &paint);
175     auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
176     EXPECT_NE(nullptr, BakedOpState::tryConstruct(allocator, *snapshot, successOp))
177             << "successOp NOT rejected by clip, so should be constructed";
178     size_t successAllocSize = allocator.usedSize();
179     EXPECT_LE(64u, successAllocSize) << "relatively large alloc for non-rejected op";
180 
181     RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, &clip, &paint);
182     EXPECT_EQ(nullptr, BakedOpState::tryConstruct(allocator, *snapshot, rejectOp))
183             << "rejectOp rejected by clip, so should not be constructed";
184 
185     // NOTE: this relies on the clip having already been serialized by the op above
186     EXPECT_EQ(successAllocSize, allocator.usedSize()) << "no extra allocation used for rejected op";
187 }
188 
TEST(BakedOpState,tryShadowOpConstruct)189 TEST(BakedOpState, tryShadowOpConstruct) {
190     Matrix4 translate10x20;
191     translate10x20.loadTranslate(10, 20, 0);
192 
193     LinearAllocator allocator;
194     {
195         auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect());  // Note: empty clip
196         BakedOpState* bakedState =
197                 BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
198 
199         EXPECT_EQ(nullptr, bakedState) << "op should be rejected by clip, so not constructed";
200         EXPECT_EQ(0u, allocator.usedSize()) << "no serialization, even for clip,"
201                                                "since op is quick rejected based on snapshot clip";
202     }
203     {
204         auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
205         BakedOpState* bakedState =
206                 BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
207 
208         ASSERT_NE(nullptr, bakedState) << "NOT rejected by clip, so op should be constructed";
209         EXPECT_LE(64u, allocator.usedSize()) << "relatively large alloc for non-rejected op";
210 
211         EXPECT_MATRIX_APPROX_EQ(translate10x20, bakedState->computedState.transform);
212         EXPECT_EQ(Rect(100, 200), bakedState->computedState.clippedBounds);
213     }
214 }
215 
TEST(BakedOpState,tryStrokeableOpConstruct)216 TEST(BakedOpState, tryStrokeableOpConstruct) {
217     LinearAllocator allocator;
218     {
219         // check regular rejection
220         SkPaint paint;
221         paint.setStyle(SkPaint::kStrokeAndFill_Style);
222         paint.setStrokeWidth(0.0f);
223         ClipRect clip(Rect(100, 200));
224         RectOp rejectOp(Rect(100, 200), Matrix4::identity(), &clip, &paint);
225         auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect());  // Note: empty clip
226         auto bakedState = BakedOpState::tryStrokeableOpConstruct(
227                 allocator, *snapshot, rejectOp, BakedOpState::StrokeBehavior::StyleDefined, false);
228 
229         EXPECT_EQ(nullptr, bakedState);
230         EXPECT_GT(8u,
231                   allocator.usedSize());  // no significant allocation space used for rejected op
232     }
233     {
234         // check simple unscaled expansion
235         SkPaint paint;
236         paint.setStyle(SkPaint::kStrokeAndFill_Style);
237         paint.setStrokeWidth(10.0f);
238         ClipRect clip(Rect(200, 200));
239         RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &paint);
240         auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200));
241         auto bakedState = BakedOpState::tryStrokeableOpConstruct(
242                 allocator, *snapshot, rejectOp, BakedOpState::StrokeBehavior::StyleDefined, false);
243 
244         ASSERT_NE(nullptr, bakedState);
245         EXPECT_EQ(Rect(45, 45, 155, 155), bakedState->computedState.clippedBounds);
246         EXPECT_EQ(0, bakedState->computedState.clipSideFlags);
247     }
248     {
249         // check simple unscaled expansion, and fill style with stroke forced
250         SkPaint paint;
251         paint.setStyle(SkPaint::kFill_Style);
252         paint.setStrokeWidth(10.0f);
253         ClipRect clip(Rect(200, 200));
254         RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &paint);
255         auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200));
256         auto bakedState = BakedOpState::tryStrokeableOpConstruct(
257                 allocator, *snapshot, rejectOp, BakedOpState::StrokeBehavior::Forced, false);
258 
259         ASSERT_NE(nullptr, bakedState);
260         EXPECT_EQ(Rect(45, 45, 155, 155), bakedState->computedState.clippedBounds);
261         EXPECT_EQ(0, bakedState->computedState.clipSideFlags);
262     }
263 }
264 
265 }  // namespace uirenderer
266 }  // namespace android
267