• 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     {
__anon1cc4ac290102() 100         1, HAIRLINE, [](const ResolvedRenderState& state) {
101             EXPECT_EQ(Rect(49.5f, 49.5f, 150.5f, 150.5f), state.clippedBounds);
102         }
103     },
104     {
__anon1cc4ac290202() 105         1, SEMI_HAIRLINE, [](const ResolvedRenderState& state) {
106             EXPECT_TRUE(state.clippedBounds.contains(49.5f, 49.5f, 150.5f, 150.5f));
107             EXPECT_TRUE(Rect(49, 49, 151, 151).contains(state.clippedBounds));
108         }
109     },
110     {
__anon1cc4ac290302() 111         1, 20, [](const ResolvedRenderState& state) {
112             EXPECT_EQ(Rect(40, 40, 160, 160), state.clippedBounds);
113         }
114     },
115 
116     // 3x3 scale:
117     {
__anon1cc4ac290402() 118         3, HAIRLINE, [](const ResolvedRenderState& state) {
119             EXPECT_EQ(Rect(149.5f, 149.5f, 200, 200), state.clippedBounds);
120             EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags);
121         }
122     },
123     {
__anon1cc4ac290502() 124         3, SEMI_HAIRLINE, [](const ResolvedRenderState& state) {
125             EXPECT_TRUE(state.clippedBounds.contains(149.5f, 149.5f, 200, 200));
126             EXPECT_TRUE(Rect(149, 149, 200, 200).contains(state.clippedBounds));
127         }
128     },
129     {
__anon1cc4ac290602() 130         3, 20, [](const ResolvedRenderState& state) {
131             EXPECT_TRUE(state.clippedBounds.contains(120, 120, 200, 200));
132             EXPECT_TRUE(Rect(119, 119, 200, 200).contains(state.clippedBounds));
133         }
134     },
135 
136     // 0.5f x 0.5f scale
137     {
__anon1cc4ac290702() 138         0.5f, HAIRLINE, [](const ResolvedRenderState& state) {
139             EXPECT_EQ(Rect(24.5f, 24.5f, 75.5f, 75.5f), state.clippedBounds);
140         }
141     },
142     {
__anon1cc4ac290802() 143         0.5f, SEMI_HAIRLINE, [](const ResolvedRenderState& state) {
144             EXPECT_TRUE(state.clippedBounds.contains(24.5f, 24.5f, 75.5f, 75.5f));
145             EXPECT_TRUE(Rect(24, 24, 76, 76).contains(state.clippedBounds));
146         }
147     },
148     {
__anon1cc4ac290902() 149         0.5f, 20, [](const ResolvedRenderState& state) {
150             EXPECT_TRUE(state.clippedBounds.contains(19.5f, 19.5f, 80.5f, 80.5f));
151             EXPECT_TRUE(Rect(19, 19, 81, 81).contains(state.clippedBounds));
152         }
153     }
154 };
155 
TEST(ResolvedRenderState,construct_expandForStroke)156 TEST(ResolvedRenderState, construct_expandForStroke) {
157     LinearAllocator allocator;
158     // Loop over table of test cases and verify different combinations of stroke width and transform
159     for (auto&& testCase : sStrokeTestCases) {
160         SkPaint strokedPaint;
161         strokedPaint.setAntiAlias(true);
162         strokedPaint.setStyle(SkPaint::kStroke_Style);
163         strokedPaint.setStrokeWidth(testCase.strokeWidth);
164 
165         ClipRect clip(Rect(200, 200));
166         RectOp recordedOp(Rect(50, 50, 150, 150),
167                 Matrix4::identity(), &clip, &strokedPaint);
168 
169         Matrix4 snapshotMatrix;
170         snapshotMatrix.loadScale(testCase.scale, testCase.scale, 1);
171         auto parentSnapshot = TestUtils::makeSnapshot(snapshotMatrix, Rect(200, 200));
172 
173         ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, true, false);
174         testCase.validator(state);
175     }
176 }
177 
TEST(BakedOpState,tryConstruct)178 TEST(BakedOpState, tryConstruct) {
179     Matrix4 translate100x0;
180     translate100x0.loadTranslate(100, 0, 0);
181 
182     SkPaint paint;
183     ClipRect clip(Rect(100, 200));
184 
185     LinearAllocator allocator;
186     RectOp successOp(Rect(30, 40, 100, 200), Matrix4::identity(), &clip, &paint);
187     auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
188     EXPECT_NE(nullptr, BakedOpState::tryConstruct(allocator, *snapshot, successOp))
189             << "successOp NOT rejected by clip, so should be constructed";
190     size_t successAllocSize = allocator.usedSize();
191     EXPECT_LE(64u, successAllocSize) << "relatively large alloc for non-rejected op";
192 
193     RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, &clip, &paint);
194     EXPECT_EQ(nullptr, BakedOpState::tryConstruct(allocator, *snapshot, rejectOp))
195             << "rejectOp rejected by clip, so should not be constructed";
196 
197     // NOTE: this relies on the clip having already been serialized by the op above
198     EXPECT_EQ(successAllocSize, allocator.usedSize()) << "no extra allocation used for rejected op";
199 }
200 
TEST(BakedOpState,tryShadowOpConstruct)201 TEST(BakedOpState, tryShadowOpConstruct) {
202     Matrix4 translate10x20;
203     translate10x20.loadTranslate(10, 20, 0);
204 
205     LinearAllocator allocator;
206     {
207         auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect()); // Note: empty clip
208         BakedOpState* bakedState = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
209 
210         EXPECT_EQ(nullptr, bakedState) << "op should be rejected by clip, so not constructed";
211         EXPECT_EQ(0u, allocator.usedSize()) << "no serialization, even for clip,"
212                 "since op is quick rejected based on snapshot clip";
213     }
214     {
215         auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
216         BakedOpState* bakedState = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
217 
218         ASSERT_NE(nullptr, bakedState) << "NOT rejected by clip, so op should be constructed";
219         EXPECT_LE(64u, allocator.usedSize()) << "relatively large alloc for non-rejected op";
220 
221         EXPECT_MATRIX_APPROX_EQ(translate10x20, bakedState->computedState.transform);
222         EXPECT_EQ(Rect(100, 200), bakedState->computedState.clippedBounds);
223     }
224 }
225 
TEST(BakedOpState,tryStrokeableOpConstruct)226 TEST(BakedOpState, tryStrokeableOpConstruct) {
227     LinearAllocator allocator;
228     {
229         // check regular rejection
230         SkPaint paint;
231         paint.setStyle(SkPaint::kStrokeAndFill_Style);
232         paint.setStrokeWidth(0.0f);
233         ClipRect clip(Rect(100, 200));
234         RectOp rejectOp(Rect(100, 200), Matrix4::identity(), &clip, &paint);
235         auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect()); // Note: empty clip
236         auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp,
237                 BakedOpState::StrokeBehavior::StyleDefined, false);
238 
239         EXPECT_EQ(nullptr, bakedState);
240         EXPECT_GT(8u, allocator.usedSize()); // no significant allocation space used for rejected op
241     }
242     {
243         // check simple unscaled expansion
244         SkPaint paint;
245         paint.setStyle(SkPaint::kStrokeAndFill_Style);
246         paint.setStrokeWidth(10.0f);
247         ClipRect clip(Rect(200, 200));
248         RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &paint);
249         auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200));
250         auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp,
251                 BakedOpState::StrokeBehavior::StyleDefined, false);
252 
253         ASSERT_NE(nullptr, bakedState);
254         EXPECT_EQ(Rect(45, 45, 155, 155), bakedState->computedState.clippedBounds);
255         EXPECT_EQ(0, bakedState->computedState.clipSideFlags);
256     }
257     {
258         // check simple unscaled expansion, and fill style with stroke forced
259         SkPaint paint;
260         paint.setStyle(SkPaint::kFill_Style);
261         paint.setStrokeWidth(10.0f);
262         ClipRect clip(Rect(200, 200));
263         RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &paint);
264         auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200));
265         auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp,
266                 BakedOpState::StrokeBehavior::Forced, false);
267 
268         ASSERT_NE(nullptr, bakedState);
269         EXPECT_EQ(Rect(45, 45, 155, 155), bakedState->computedState.clippedBounds);
270         EXPECT_EQ(0, bakedState->computedState.clipSideFlags);
271     }
272 }
273 
274 } // namespace uirenderer
275 } // namespace android
276