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 <SkPath.h>
     18 #include <SkRegion.h>
     19 #include <gtest/gtest.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,
    198                   area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity()));
    199     }
    200 
    201     // rect
    202     {
    203         ClipRect recordedClip(Rect(100, 100));
    204         Matrix4 translateScale;
    205         translateScale.loadTranslate(100, 100, 0);
    206         translateScale.scale(2, 3, 1);
    207         auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale);
    208         ASSERT_NE(nullptr, resolvedClip);
    209         ASSERT_EQ(ClipMode::Rectangle, resolvedClip->mode);
    210         EXPECT_EQ(Rect(100, 100, 200, 200), resolvedClip->rect);
    211 
    212         EXPECT_EQ(resolvedClip,
    213                   area.serializeIntersectedClip(allocator, &recordedClip, translateScale))
    214                 << "Must return previous serialization, since input is same";
    215 
    216         ClipRect recordedClip2(Rect(100, 100));
    217         EXPECT_NE(resolvedClip,
    218                   area.serializeIntersectedClip(allocator, &recordedClip2, translateScale))
    219                 << "Shouldn't return previous serialization, since matrix location is different";
    220     }
    221 
    222     // rect list
    223     Matrix4 rotate;
    224     rotate.loadRotate(2.0f);
    225     area.clipRectWithTransform(Rect(200, 200), &rotate, SkRegion::kIntersect_Op);
    226     {
    227         ClipRect recordedClip(Rect(100, 100));
    228         auto resolvedClip =
    229                 area.serializeIntersectedClip(allocator, &recordedClip, Matrix4::identity());
    230         ASSERT_NE(nullptr, resolvedClip);
    231         ASSERT_EQ(ClipMode::RectangleList, resolvedClip->mode);
    232         auto clipRectList = reinterpret_cast<const ClipRectList*>(resolvedClip);
    233         EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount());
    234     }
    235 
    236     // region
    237     SkPath circlePath;
    238     circlePath.addCircle(100, 100, 100);
    239     area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op);
    240     {
    241         SkPath ovalPath;
    242         ovalPath.addOval(SkRect::MakeLTRB(50, 0, 150, 200));
    243 
    244         ClipRegion recordedClip;
    245         recordedClip.region.setPath(ovalPath, SkRegion(SkIRect::MakeWH(200, 200)));
    246         recordedClip.rect = Rect(200, 200);
    247 
    248         Matrix4 translate10x20;
    249         translate10x20.loadTranslate(10, 20, 0);
    250         auto resolvedClip = area.serializeIntersectedClip(
    251                 allocator, &recordedClip,
    252                 translate10x20);  // Note: only translate for now, others not handled correctly
    253         ASSERT_NE(nullptr, resolvedClip);
    254         ASSERT_EQ(ClipMode::Region, resolvedClip->mode);
    255         auto clipRegion = reinterpret_cast<const ClipRegion*>(resolvedClip);
    256         EXPECT_EQ(SkIRect::MakeLTRB(60, 20, 160, 200), clipRegion->region.getBounds());
    257     }
    258 }
    259 
    260 TEST(ClipArea, serializeIntersectedClip_snap) {
    261     ClipArea area(createClipArea());
    262     area.setClip(100.2, 100.4, 500.6, 500.8);
    263     LinearAllocator allocator;
    264 
    265     {
    266         // no recorded clip case
    267         auto resolvedClip = area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity());
    268         EXPECT_EQ(Rect(100, 100, 501, 501), resolvedClip->rect);
    269     }
    270     {
    271         // recorded clip case
    272         ClipRect recordedClip(Rect(100.12, 100.74));
    273         Matrix4 translateScale;
    274         translateScale.loadTranslate(100, 100, 0);
    275         translateScale.scale(2, 3,
    276                              1);  // recorded clip will have non-int coords, even after transform
    277         auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale);
    278         ASSERT_NE(nullptr, resolvedClip);
    279         EXPECT_EQ(ClipMode::Rectangle, resolvedClip->mode);
    280         EXPECT_EQ(Rect(100, 100, 300, 402), resolvedClip->rect);
    281     }
    282 }
    283 
    284 TEST(ClipArea, serializeIntersectedClip_scale) {
    285     ClipArea area(createClipArea());
    286     area.setClip(0, 0, 400, 400);
    287     LinearAllocator allocator;
    288 
    289     SkPath circlePath;
    290     circlePath.addCircle(50, 50, 50);
    291 
    292     ClipRegion recordedClip;
    293     recordedClip.region.setPath(circlePath, SkRegion(SkIRect::MakeWH(100, 100)));
    294     recordedClip.rect = Rect(100, 100);
    295 
    296     Matrix4 translateScale;
    297     translateScale.loadTranslate(100, 100, 0);
    298     translateScale.scale(2, 2, 1);
    299     auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale);
    300 
    301     ASSERT_NE(nullptr, resolvedClip);
    302     EXPECT_EQ(ClipMode::Region, resolvedClip->mode);
    303     EXPECT_EQ(Rect(100, 100, 300, 300), resolvedClip->rect);
    304     auto clipRegion = reinterpret_cast<const ClipRegion*>(resolvedClip);
    305     EXPECT_EQ(SkIRect::MakeLTRB(100, 100, 300, 300), clipRegion->region.getBounds());
    306 }
    307 
    308 TEST(ClipArea, applyTransformToRegion_identity) {
    309     SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
    310     ClipArea::applyTransformToRegion(Matrix4::identity(), &region);
    311     EXPECT_TRUE(region.isRect());
    312     EXPECT_EQ(SkIRect::MakeLTRB(1, 2, 3, 4), region.getBounds());
    313 }
    314 
    315 TEST(ClipArea, applyTransformToRegion_translate) {
    316     SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
    317     Matrix4 transform;
    318     transform.loadTranslate(10, 20, 0);
    319     ClipArea::applyTransformToRegion(transform, &region);
    320     EXPECT_TRUE(region.isRect());
    321     EXPECT_EQ(SkIRect::MakeLTRB(11, 22, 13, 24), region.getBounds());
    322 }
    323 
    324 TEST(ClipArea, applyTransformToRegion_scale) {
    325     SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
    326     Matrix4 transform;
    327     transform.loadScale(2, 3, 1);
    328     ClipArea::applyTransformToRegion(transform, &region);
    329     EXPECT_TRUE(region.isRect());
    330     EXPECT_EQ(SkIRect::MakeLTRB(2, 6, 6, 12), region.getBounds());
    331 }
    332 
    333 TEST(ClipArea, applyTransformToRegion_translateScale) {
    334     SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
    335     Matrix4 transform;
    336     transform.translate(10, 20);
    337     transform.scale(2, 3, 1);
    338     ClipArea::applyTransformToRegion(transform, &region);
    339     EXPECT_TRUE(region.isRect());
    340     EXPECT_EQ(SkIRect::MakeLTRB(12, 26, 16, 32), region.getBounds());
    341 }
    342 
    343 TEST(ClipArea, applyTransformToRegion_rotate90) {
    344     SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
    345     Matrix4 transform;
    346     transform.loadRotate(90);
    347     ClipArea::applyTransformToRegion(transform, &region);
    348     EXPECT_TRUE(region.isRect());
    349     EXPECT_EQ(SkIRect::MakeLTRB(-4, 1, -2, 3), region.getBounds());
    350 }
    351 
    352 }  // namespace uirenderer
    353 }  // namespace android
    354