Home | History | Annotate | Download | only in unit
      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 
     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 
     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     {
    100         1, HAIRLINE, [](const ResolvedRenderState& state) {
    101             EXPECT_EQ(Rect(49.5f, 49.5f, 150.5f, 150.5f), state.clippedBounds);
    102         }
    103     },
    104     {
    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     {
    111         1, 20, [](const ResolvedRenderState& state) {
    112             EXPECT_EQ(Rect(40, 40, 160, 160), state.clippedBounds);
    113         }
    114     },
    115 
    116     // 3x3 scale:
    117     {
    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     {
    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     {
    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     {
    138         0.5f, HAIRLINE, [](const ResolvedRenderState& state) {
    139             EXPECT_EQ(Rect(24.5f, 24.5f, 75.5f, 75.5f), state.clippedBounds);
    140         }
    141     },
    142     {
    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     {
    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 
    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 
    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 
    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 
    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