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 #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(), &region);
    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, &region);
    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, &region);
    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, &region);
    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, &region);
    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