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 <DeferredLayerUpdater.h>
     20 #include <RecordedOp.h>
     21 #include <RecordingCanvas.h>
     22 #include <hwui/Paint.h>
     23 #include <minikin/Layout.h>
     24 #include <tests/common/TestUtils.h>
     25 #include <utils/Color.h>
     26 
     27 #include <SkGradientShader.h>
     28 #include <SkShader.h>
     29 
     30 namespace android {
     31 namespace uirenderer {
     32 
     33 static void playbackOps(const DisplayList& displayList,
     34         std::function<void(const RecordedOp&)> opReceiver) {
     35     for (auto& chunk : displayList.getChunks()) {
     36         for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
     37             RecordedOp* op = displayList.getOps()[opIndex];
     38             opReceiver(*op);
     39         }
     40     }
     41 }
     42 
     43 static void validateSingleOp(std::unique_ptr<DisplayList>& dl,
     44         std::function<void(const RecordedOp& op)> opValidator) {
     45     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
     46     opValidator(*(dl->getOps()[0]));
     47 }
     48 
     49 TEST(RecordingCanvas, emptyPlayback) {
     50     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
     51         canvas.save(SaveFlags::MatrixClip);
     52         canvas.restore();
     53     });
     54     playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); });
     55 }
     56 
     57 TEST(RecordingCanvas, clipRect) {
     58     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
     59         canvas.save(SaveFlags::MatrixClip);
     60         canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op);
     61         canvas.drawRect(0, 0, 50, 50, SkPaint());
     62         canvas.drawRect(50, 50, 100, 100, SkPaint());
     63         canvas.restore();
     64     });
     65 
     66     ASSERT_EQ(2u, dl->getOps().size()) << "Must be exactly two ops";
     67     EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[0]->localClip);
     68     EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[1]->localClip);
     69     EXPECT_EQ(dl->getOps()[0]->localClip, dl->getOps()[1]->localClip)
     70             << "Clip should be serialized once";
     71 }
     72 
     73 TEST(RecordingCanvas, emptyClipRect) {
     74     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
     75         canvas.save(SaveFlags::MatrixClip);
     76         canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op);
     77         canvas.clipRect(100, 100, 200, 200, SkRegion::kIntersect_Op);
     78         canvas.drawRect(0, 0, 50, 50, SkPaint()); // rejected at record time
     79         canvas.restore();
     80     });
     81     ASSERT_EQ(0u, dl->getOps().size()) << "Must be zero ops. Rect should be rejected.";
     82 }
     83 
     84 TEST(RecordingCanvas, emptyPaintRejection) {
     85     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
     86         SkPaint emptyPaint;
     87         emptyPaint.setColor(Color::Transparent);
     88 
     89         float points[] = {0, 0, 200, 200};
     90         canvas.drawPoints(points, 4, emptyPaint);
     91         canvas.drawLines(points, 4, emptyPaint);
     92         canvas.drawRect(0, 0, 200, 200, emptyPaint);
     93         canvas.drawRegion(SkRegion(SkIRect::MakeWH(200, 200)), emptyPaint);
     94         canvas.drawRoundRect(0, 0, 200, 200, 10, 10, emptyPaint);
     95         canvas.drawCircle(100, 100, 100, emptyPaint);
     96         canvas.drawOval(0, 0, 200, 200, emptyPaint);
     97         canvas.drawArc(0, 0, 200, 200, 0, 360, true, emptyPaint);
     98         SkPath path;
     99         path.addRect(0, 0, 200, 200);
    100         canvas.drawPath(path, emptyPaint);
    101     });
    102     EXPECT_EQ(0u, dl->getOps().size()) << "Op should be rejected";
    103 }
    104 
    105 TEST(RecordingCanvas, drawArc) {
    106     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
    107         canvas.drawArc(0, 0, 200, 200, 0, 180, true, SkPaint());
    108         canvas.drawArc(0, 0, 100, 100, 0, 360, true, SkPaint());
    109     });
    110 
    111     auto&& ops = dl->getOps();
    112     ASSERT_EQ(2u, ops.size()) << "Must be exactly two ops";
    113     EXPECT_EQ(RecordedOpId::ArcOp, ops[0]->opId);
    114     EXPECT_EQ(Rect(200, 200), ops[0]->unmappedBounds);
    115 
    116     EXPECT_EQ(RecordedOpId::OvalOp, ops[1]->opId)
    117             << "Circular arcs should be converted to ovals";
    118     EXPECT_EQ(Rect(100, 100), ops[1]->unmappedBounds);
    119 }
    120 
    121 TEST(RecordingCanvas, drawLines) {
    122     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
    123         SkPaint paint;
    124         paint.setStrokeWidth(20); // doesn't affect recorded bounds - would be resolved at bake time
    125         float points[] = { 0, 0, 20, 10, 30, 40, 90 }; // NB: only 1 valid line
    126         canvas.drawLines(&points[0], 7, paint);
    127     });
    128 
    129     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
    130     auto op = dl->getOps()[0];
    131     ASSERT_EQ(RecordedOpId::LinesOp, op->opId);
    132     EXPECT_EQ(4, ((LinesOp*)op)->floatCount)
    133             << "float count must be rounded down to closest multiple of 4";
    134     EXPECT_EQ(Rect(20, 10), op->unmappedBounds)
    135             << "unmapped bounds must be size of line, and not outset for stroke width";
    136 }
    137 
    138 TEST(RecordingCanvas, drawRect) {
    139     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
    140         canvas.drawRect(10, 20, 90, 180, SkPaint());
    141     });
    142 
    143     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
    144     auto op = *(dl->getOps()[0]);
    145     ASSERT_EQ(RecordedOpId::RectOp, op.opId);
    146     EXPECT_EQ(nullptr, op.localClip);
    147     EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
    148 }
    149 
    150 TEST(RecordingCanvas, drawRoundRect) {
    151     // Round case - stays rounded
    152     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
    153         canvas.drawRoundRect(0, 0, 100, 100, 10, 10, SkPaint());
    154     });
    155     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
    156     ASSERT_EQ(RecordedOpId::RoundRectOp, dl->getOps()[0]->opId);
    157 
    158     // Non-rounded case - turned into drawRect
    159     dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
    160         canvas.drawRoundRect(0, 0, 100, 100, 0, -1, SkPaint());
    161     });
    162     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
    163     ASSERT_EQ(RecordedOpId::RectOp, dl->getOps()[0]->opId)
    164         << "Non-rounded rects should be converted";
    165 }
    166 
    167 TEST(RecordingCanvas, drawGlyphs) {
    168     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
    169         SkPaint paint;
    170         paint.setAntiAlias(true);
    171         paint.setTextSize(20);
    172         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    173         TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
    174     });
    175 
    176     int count = 0;
    177     playbackOps(*dl, [&count](const RecordedOp& op) {
    178         count++;
    179         ASSERT_EQ(RecordedOpId::TextOp, op.opId);
    180         EXPECT_EQ(nullptr, op.localClip);
    181         EXPECT_TRUE(op.localMatrix.isIdentity());
    182         EXPECT_TRUE(op.unmappedBounds.contains(25, 15, 50, 25))
    183                 << "Op expected to be 25+ pixels wide, 10+ pixels tall";
    184     });
    185     ASSERT_EQ(1, count);
    186 }
    187 
    188 TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) {
    189     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
    190         SkPaint paint;
    191         paint.setAntiAlias(true);
    192         paint.setTextSize(20);
    193         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    194         for (int i = 0; i < 2; i++) {
    195             for (int j = 0; j < 2; j++) {
    196                 paint.setUnderlineText(i != 0);
    197                 paint.setStrikeThruText(j != 0);
    198                 TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
    199             }
    200         }
    201     });
    202 
    203     auto ops = dl->getOps();
    204     ASSERT_EQ(8u, ops.size());
    205 
    206     int index = 0;
    207     EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); // no underline or strikethrough
    208 
    209     EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
    210     EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough only
    211 
    212     EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
    213     EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline only
    214 
    215     EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
    216     EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline
    217     EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough
    218 }
    219 
    220 TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) {
    221     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
    222         SkPaint paint;
    223         paint.setAntiAlias(true);
    224         paint.setTextSize(20);
    225         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    226         paint.setTextAlign(SkPaint::kLeft_Align);
    227         TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
    228         paint.setTextAlign(SkPaint::kCenter_Align);
    229         TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
    230         paint.setTextAlign(SkPaint::kRight_Align);
    231         TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
    232     });
    233 
    234     int count = 0;
    235     float lastX = FLT_MAX;
    236     playbackOps(*dl, [&count, &lastX](const RecordedOp& op) {
    237         count++;
    238         ASSERT_EQ(RecordedOpId::TextOp, op.opId);
    239         EXPECT_EQ(SkPaint::kLeft_Align, op.paint->getTextAlign())
    240                 << "recorded drawText commands must force kLeft_Align on their paint";
    241 
    242         // verify TestUtils alignment offsetting (TODO: move asserts to Canvas base class)
    243         EXPECT_GT(lastX, ((const TextOp&)op).x)
    244                 << "x coordinate should reduce across each of the draw commands, from alignment";
    245         lastX = ((const TextOp&)op).x;
    246     });
    247     ASSERT_EQ(3, count);
    248 }
    249 
    250 TEST(RecordingCanvas, drawColor) {
    251     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
    252         canvas.drawColor(Color::Black, SkXfermode::kSrcOver_Mode);
    253     });
    254 
    255     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
    256     auto op = *(dl->getOps()[0]);
    257     EXPECT_EQ(RecordedOpId::ColorOp, op.opId);
    258     EXPECT_EQ(nullptr, op.localClip);
    259     EXPECT_TRUE(op.unmappedBounds.isEmpty()) << "Expect undefined recorded bounds";
    260 }
    261 
    262 TEST(RecordingCanvas, backgroundAndImage) {
    263     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
    264         SkBitmap bitmap;
    265         bitmap.setInfo(SkImageInfo::MakeUnknown(25, 25));
    266         SkPaint paint;
    267         paint.setColor(SK_ColorBLUE);
    268 
    269         canvas.save(SaveFlags::MatrixClip);
    270         {
    271             // a background!
    272             canvas.save(SaveFlags::MatrixClip);
    273             canvas.drawRect(0, 0, 100, 200, paint);
    274             canvas.restore();
    275         }
    276         {
    277             // an image!
    278             canvas.save(SaveFlags::MatrixClip);
    279             canvas.translate(25, 25);
    280             canvas.scale(2, 2);
    281             canvas.drawBitmap(bitmap, 0, 0, nullptr);
    282             canvas.restore();
    283         }
    284         canvas.restore();
    285     });
    286 
    287     int count = 0;
    288     playbackOps(*dl, [&count](const RecordedOp& op) {
    289         if (count == 0) {
    290             ASSERT_EQ(RecordedOpId::RectOp, op.opId);
    291             ASSERT_NE(nullptr, op.paint);
    292             EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
    293             EXPECT_EQ(Rect(100, 200), op.unmappedBounds);
    294             EXPECT_EQ(nullptr, op.localClip);
    295 
    296             Matrix4 expectedMatrix;
    297             expectedMatrix.loadIdentity();
    298             EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
    299         } else {
    300             ASSERT_EQ(RecordedOpId::BitmapOp, op.opId);
    301             EXPECT_EQ(nullptr, op.paint);
    302             EXPECT_EQ(Rect(25, 25), op.unmappedBounds);
    303             EXPECT_EQ(nullptr, op.localClip);
    304 
    305             Matrix4 expectedMatrix;
    306             expectedMatrix.loadTranslate(25, 25, 0);
    307             expectedMatrix.scale(2, 2, 1);
    308             EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
    309         }
    310         count++;
    311     });
    312     ASSERT_EQ(2, count);
    313 }
    314 
    315 RENDERTHREAD_TEST(RecordingCanvas, textureLayer) {
    316     auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
    317             SkMatrix::MakeTrans(5, 5));
    318 
    319     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200,
    320             [&layerUpdater](RecordingCanvas& canvas) {
    321         canvas.drawLayer(layerUpdater.get());
    322     });
    323 
    324     validateSingleOp(dl, [] (const RecordedOp& op) {
    325         ASSERT_EQ(RecordedOpId::TextureLayerOp, op.opId);
    326         ASSERT_TRUE(op.localMatrix.isIdentity()) << "Op must not apply matrix at record time.";
    327     });
    328 }
    329 
    330 TEST(RecordingCanvas, saveLayer_simple) {
    331     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
    332         canvas.saveLayerAlpha(10, 20, 190, 180, 128, SaveFlags::ClipToLayer);
    333         canvas.drawRect(10, 20, 190, 180, SkPaint());
    334         canvas.restore();
    335     });
    336     int count = 0;
    337     playbackOps(*dl, [&count](const RecordedOp& op) {
    338         Matrix4 expectedMatrix;
    339         switch(count++) {
    340         case 0:
    341             EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId);
    342             EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
    343             EXPECT_EQ(nullptr, op.localClip);
    344             EXPECT_TRUE(op.localMatrix.isIdentity());
    345             break;
    346         case 1:
    347             EXPECT_EQ(RecordedOpId::RectOp, op.opId);
    348             EXPECT_CLIP_RECT(Rect(180, 160), op.localClip);
    349             EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
    350             expectedMatrix.loadTranslate(-10, -20, 0);
    351             EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
    352             break;
    353         case 2:
    354             EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
    355             // Don't bother asserting recording state data - it's not used
    356             break;
    357         default:
    358             ADD_FAILURE();
    359         }
    360     });
    361     EXPECT_EQ(3, count);
    362 }
    363 
    364 TEST(RecordingCanvas, saveLayer_rounding) {
    365     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
    366             canvas.saveLayerAlpha(10.25f, 10.75f, 89.25f, 89.75f, 128, SaveFlags::ClipToLayer);
    367             canvas.drawRect(20, 20, 80, 80, SkPaint());
    368             canvas.restore();
    369         });
    370         int count = 0;
    371         playbackOps(*dl, [&count](const RecordedOp& op) {
    372             Matrix4 expectedMatrix;
    373             switch(count++) {
    374             case 0:
    375                 EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId);
    376                 EXPECT_EQ(Rect(10, 10, 90, 90), op.unmappedBounds) << "Expect bounds rounded out";
    377                 break;
    378             case 1:
    379                 EXPECT_EQ(RecordedOpId::RectOp, op.opId);
    380                 expectedMatrix.loadTranslate(-10, -10, 0);
    381                 EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix) << "Expect rounded offset";
    382                 break;
    383             case 2:
    384                 EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
    385                 // Don't bother asserting recording state data - it's not used
    386                 break;
    387             default:
    388                 ADD_FAILURE();
    389             }
    390         });
    391         EXPECT_EQ(3, count);
    392 }
    393 
    394 TEST(RecordingCanvas, saveLayer_missingRestore) {
    395     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
    396         canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
    397         canvas.drawRect(0, 0, 200, 200, SkPaint());
    398         // Note: restore omitted, shouldn't result in unmatched save
    399     });
    400     int count = 0;
    401     playbackOps(*dl, [&count](const RecordedOp& op) {
    402         if (count++ == 2) {
    403             EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
    404         }
    405     });
    406     EXPECT_EQ(3, count) << "Missing a restore shouldn't result in an unmatched saveLayer";
    407 }
    408 
    409 TEST(RecordingCanvas, saveLayer_simpleUnclipped) {
    410     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
    411         canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
    412         canvas.drawRect(10, 20, 190, 180, SkPaint());
    413         canvas.restore();
    414     });
    415     int count = 0;
    416     playbackOps(*dl, [&count](const RecordedOp& op) {
    417         switch(count++) {
    418         case 0:
    419             EXPECT_EQ(RecordedOpId::BeginUnclippedLayerOp, op.opId);
    420             EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
    421             EXPECT_EQ(nullptr, op.localClip);
    422             EXPECT_TRUE(op.localMatrix.isIdentity());
    423             break;
    424         case 1:
    425             EXPECT_EQ(RecordedOpId::RectOp, op.opId);
    426             EXPECT_EQ(nullptr, op.localClip);
    427             EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
    428             EXPECT_TRUE(op.localMatrix.isIdentity());
    429             break;
    430         case 2:
    431             EXPECT_EQ(RecordedOpId::EndUnclippedLayerOp, op.opId);
    432             // Don't bother asserting recording state data - it's not used
    433             break;
    434         default:
    435             ADD_FAILURE();
    436         }
    437     });
    438     EXPECT_EQ(3, count);
    439 }
    440 
    441 TEST(RecordingCanvas, saveLayer_addClipFlag) {
    442     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
    443         canvas.save(SaveFlags::MatrixClip);
    444         canvas.clipRect(10, 20, 190, 180, SkRegion::kIntersect_Op);
    445         canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
    446         canvas.drawRect(10, 20, 190, 180, SkPaint());
    447         canvas.restore();
    448         canvas.restore();
    449     });
    450     int count = 0;
    451     playbackOps(*dl, [&count](const RecordedOp& op) {
    452         if (count++ == 0) {
    453             EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId)
    454                     << "Clip + unclipped saveLayer should result in a clipped layer";
    455         }
    456     });
    457     EXPECT_EQ(3, count);
    458 }
    459 
    460 TEST(RecordingCanvas, saveLayer_viewportCrop) {
    461     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
    462         // shouldn't matter, since saveLayer will clip to its bounds
    463         canvas.clipRect(-1000, -1000, 1000, 1000, SkRegion::kReplace_Op);
    464 
    465         canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
    466         canvas.drawRect(0, 0, 400, 400, SkPaint());
    467         canvas.restore();
    468     });
    469     int count = 0;
    470     playbackOps(*dl, [&count](const RecordedOp& op) {
    471         if (count++ == 1) {
    472             Matrix4 expectedMatrix;
    473             EXPECT_EQ(RecordedOpId::RectOp, op.opId);
    474             EXPECT_CLIP_RECT(Rect(100, 100), op.localClip) // Recorded clip rect should be
    475             // intersection of viewport and saveLayer bounds, in layer space;
    476             EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
    477             expectedMatrix.loadTranslate(-100, -100, 0);
    478             EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
    479         }
    480     });
    481     EXPECT_EQ(3, count);
    482 }
    483 
    484 TEST(RecordingCanvas, saveLayer_rotateUnclipped) {
    485     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
    486         canvas.save(SaveFlags::MatrixClip);
    487         canvas.translate(100, 100);
    488         canvas.rotate(45);
    489         canvas.translate(-50, -50);
    490 
    491         canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer);
    492         canvas.drawRect(0, 0, 100, 100, SkPaint());
    493         canvas.restore();
    494 
    495         canvas.restore();
    496     });
    497     int count = 0;
    498     playbackOps(*dl, [&count](const RecordedOp& op) {
    499         if (count++ == 1) {
    500             EXPECT_EQ(RecordedOpId::RectOp, op.opId);
    501             EXPECT_CLIP_RECT(Rect(100, 100), op.localClip);
    502             EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
    503             EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.localMatrix)
    504                     << "Recorded op shouldn't see any canvas transform before the saveLayer";
    505         }
    506     });
    507     EXPECT_EQ(3, count);
    508 }
    509 
    510 TEST(RecordingCanvas, saveLayer_rotateClipped) {
    511     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
    512         canvas.save(SaveFlags::MatrixClip);
    513         canvas.translate(100, 100);
    514         canvas.rotate(45);
    515         canvas.translate(-200, -200);
    516 
    517         // area of saveLayer will be clipped to parent viewport, so we ask for 400x400...
    518         canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
    519         canvas.drawRect(0, 0, 400, 400, SkPaint());
    520         canvas.restore();
    521 
    522         canvas.restore();
    523     });
    524     int count = 0;
    525     playbackOps(*dl, [&count](const RecordedOp& op) {
    526         if (count++ == 1) {
    527             Matrix4 expectedMatrix;
    528             EXPECT_EQ(RecordedOpId::RectOp, op.opId);
    529 
    530             // ...and get about 58.6, 58.6, 341.4 341.4, because the bounds are clipped by
    531             // the parent 200x200 viewport, but prior to rotation
    532             ASSERT_NE(nullptr, op.localClip);
    533             ASSERT_EQ(ClipMode::Rectangle, op.localClip->mode);
    534             // NOTE: this check relies on saveLayer altering the clip post-viewport init. This
    535             // causes the clip to be recorded by contained draw commands, though it's not necessary
    536             // since the same clip will be computed at draw time. If such a change is made, this
    537             // check could be done at record time by querying the clip, or the clip could be altered
    538             // slightly so that it is serialized.
    539             EXPECT_EQ(Rect(59, 59, 341, 341), op.localClip->rect);
    540             EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
    541             expectedMatrix.loadIdentity();
    542             EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
    543         }
    544     });
    545     EXPECT_EQ(3, count);
    546 }
    547 
    548 TEST(RecordingCanvas, saveLayer_rejectBegin) {
    549     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
    550         canvas.save(SaveFlags::MatrixClip);
    551         canvas.translate(0, -20); // avoid identity case
    552         // empty clip rect should force layer + contents to be rejected
    553         canvas.clipRect(0, -20, 200, -20, SkRegion::kIntersect_Op);
    554         canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
    555         canvas.drawRect(0, 0, 200, 200, SkPaint());
    556         canvas.restore();
    557         canvas.restore();
    558     });
    559 
    560     ASSERT_EQ(0u, dl->getOps().size()) << "Begin/Rect/End should all be rejected.";
    561 }
    562 
    563 TEST(RecordingCanvas, drawRenderNode_rejection) {
    564     auto child = TestUtils::createNode(50, 50, 150, 150,
    565             [](RenderProperties& props, RecordingCanvas& canvas) {
    566         SkPaint paint;
    567         paint.setColor(SK_ColorWHITE);
    568         canvas.drawRect(0, 0, 100, 100, paint);
    569     });
    570 
    571     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&child](RecordingCanvas& canvas) {
    572         canvas.clipRect(0, 0, 0, 0, SkRegion::kIntersect_Op); // empty clip, reject node
    573         canvas.drawRenderNode(child.get()); // shouldn't crash when rejecting node...
    574     });
    575     ASSERT_TRUE(dl->isEmpty());
    576 }
    577 
    578 TEST(RecordingCanvas, drawRenderNode_projection) {
    579     sp<RenderNode> background = TestUtils::createNode(50, 50, 150, 150,
    580             [](RenderProperties& props, RecordingCanvas& canvas) {
    581         SkPaint paint;
    582         paint.setColor(SK_ColorWHITE);
    583         canvas.drawRect(0, 0, 100, 100, paint);
    584     });
    585     {
    586         background->mutateStagingProperties().setProjectionReceiver(false);
    587 
    588         // NO RECEIVER PRESENT
    589         auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200,
    590                     [&background](RecordingCanvas& canvas) {
    591             canvas.drawRect(0, 0, 100, 100, SkPaint());
    592             canvas.drawRenderNode(background.get());
    593             canvas.drawRect(0, 0, 100, 100, SkPaint());
    594         });
    595         EXPECT_EQ(-1, dl->projectionReceiveIndex)
    596                 << "no projection receiver should have been observed";
    597     }
    598     {
    599         background->mutateStagingProperties().setProjectionReceiver(true);
    600 
    601         // RECEIVER PRESENT
    602         auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200,
    603                     [&background](RecordingCanvas& canvas) {
    604             canvas.drawRect(0, 0, 100, 100, SkPaint());
    605             canvas.drawRenderNode(background.get());
    606             canvas.drawRect(0, 0, 100, 100, SkPaint());
    607         });
    608 
    609         ASSERT_EQ(3u, dl->getOps().size()) << "Must be three ops";
    610         auto op = dl->getOps()[1];
    611         EXPECT_EQ(RecordedOpId::RenderNodeOp, op->opId);
    612         EXPECT_EQ(1, dl->projectionReceiveIndex)
    613                 << "correct projection receiver not identified";
    614 
    615         // verify the behavior works even though projection receiver hasn't been sync'd yet
    616         EXPECT_TRUE(background->stagingProperties().isProjectionReceiver());
    617         EXPECT_FALSE(background->properties().isProjectionReceiver());
    618     }
    619 }
    620 
    621 TEST(RecordingCanvas, firstClipWillReplace) {
    622     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
    623         canvas.save(SaveFlags::MatrixClip);
    624         // since no explicit clip set on canvas, this should be the one observed on op:
    625         canvas.clipRect(-100, -100, 300, 300, SkRegion::kIntersect_Op);
    626 
    627         SkPaint paint;
    628         paint.setColor(SK_ColorWHITE);
    629         canvas.drawRect(0, 0, 100, 100, paint);
    630 
    631         canvas.restore();
    632     });
    633     ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op";
    634     // first clip must be preserved, even if it extends beyond canvas bounds
    635     EXPECT_CLIP_RECT(Rect(-100, -100, 300, 300), dl->getOps()[0]->localClip);
    636 }
    637 
    638 TEST(RecordingCanvas, replaceClipIntersectWithRoot) {
    639     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
    640         canvas.save(SaveFlags::MatrixClip);
    641         canvas.clipRect(-10, -10, 110, 110, SkRegion::kReplace_Op);
    642         canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
    643         canvas.restore();
    644     });
    645     ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op";
    646     // first clip must be preserved, even if it extends beyond canvas bounds
    647     EXPECT_CLIP_RECT(Rect(-10, -10, 110, 110), dl->getOps()[0]->localClip);
    648     EXPECT_TRUE(dl->getOps()[0]->localClip->intersectWithRoot);
    649 }
    650 
    651 TEST(RecordingCanvas, insertReorderBarrier) {
    652     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
    653         canvas.drawRect(0, 0, 400, 400, SkPaint());
    654         canvas.insertReorderBarrier(true);
    655         canvas.insertReorderBarrier(false);
    656         canvas.insertReorderBarrier(false);
    657         canvas.insertReorderBarrier(true);
    658         canvas.drawRect(0, 0, 400, 400, SkPaint());
    659         canvas.insertReorderBarrier(false);
    660     });
    661 
    662     auto chunks = dl->getChunks();
    663     EXPECT_EQ(0u, chunks[0].beginOpIndex);
    664     EXPECT_EQ(1u, chunks[0].endOpIndex);
    665     EXPECT_FALSE(chunks[0].reorderChildren);
    666 
    667     EXPECT_EQ(1u, chunks[1].beginOpIndex);
    668     EXPECT_EQ(2u, chunks[1].endOpIndex);
    669     EXPECT_TRUE(chunks[1].reorderChildren);
    670 }
    671 
    672 TEST(RecordingCanvas, insertReorderBarrier_clip) {
    673     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
    674         // first chunk: no recorded clip
    675         canvas.insertReorderBarrier(true);
    676         canvas.drawRect(0, 0, 400, 400, SkPaint());
    677 
    678         // second chunk: no recorded clip, since inorder region
    679         canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
    680         canvas.insertReorderBarrier(false);
    681         canvas.drawRect(0, 0, 400, 400, SkPaint());
    682 
    683         // third chunk: recorded clip
    684         canvas.insertReorderBarrier(true);
    685         canvas.drawRect(0, 0, 400, 400, SkPaint());
    686     });
    687 
    688     auto chunks = dl->getChunks();
    689     ASSERT_EQ(3u, chunks.size());
    690 
    691     EXPECT_TRUE(chunks[0].reorderChildren);
    692     EXPECT_EQ(nullptr, chunks[0].reorderClip);
    693 
    694     EXPECT_FALSE(chunks[1].reorderChildren);
    695     EXPECT_EQ(nullptr, chunks[1].reorderClip);
    696 
    697     EXPECT_TRUE(chunks[2].reorderChildren);
    698     ASSERT_NE(nullptr, chunks[2].reorderClip);
    699     EXPECT_EQ(Rect(200, 200), chunks[2].reorderClip->rect);
    700 }
    701 
    702 TEST(RecordingCanvas, refPaint) {
    703     SkPaint paint;
    704 
    705     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&paint](RecordingCanvas& canvas) {
    706         paint.setColor(SK_ColorBLUE);
    707         // first two should use same paint
    708         canvas.drawRect(0, 0, 200, 10, paint);
    709         SkPaint paintCopy(paint);
    710         canvas.drawRect(0, 10, 200, 20, paintCopy);
    711 
    712         // only here do we use different paint ptr
    713         paint.setColor(SK_ColorRED);
    714         canvas.drawRect(0, 20, 200, 30, paint);
    715     });
    716     auto ops = dl->getOps();
    717     ASSERT_EQ(3u, ops.size());
    718 
    719     // first two are the same
    720     EXPECT_NE(nullptr, ops[0]->paint);
    721     EXPECT_NE(&paint, ops[0]->paint);
    722     EXPECT_EQ(ops[0]->paint, ops[1]->paint);
    723 
    724     // last is different, but still copied / non-null
    725     EXPECT_NE(nullptr, ops[2]->paint);
    726     EXPECT_NE(ops[0]->paint, ops[2]->paint);
    727     EXPECT_NE(&paint, ops[2]->paint);
    728 }
    729 
    730 TEST(RecordingCanvas, refBitmap) {
    731     SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
    732     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
    733         canvas.drawBitmap(bitmap, 0, 0, nullptr);
    734     });
    735     auto& bitmaps = dl->getBitmapResources();
    736     EXPECT_EQ(1u, bitmaps.size());
    737 }
    738 
    739 TEST(RecordingCanvas, refBitmapInShader_bitmapShader) {
    740     SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
    741     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
    742         SkPaint paint;
    743         SkAutoTUnref<SkShader> shader(SkShader::CreateBitmapShader(bitmap,
    744                 SkShader::TileMode::kClamp_TileMode,
    745                 SkShader::TileMode::kClamp_TileMode));
    746         paint.setShader(shader);
    747         canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
    748     });
    749     auto& bitmaps = dl->getBitmapResources();
    750     EXPECT_EQ(1u, bitmaps.size());
    751 }
    752 
    753 TEST(RecordingCanvas, refBitmapInShader_composeShader) {
    754     SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
    755     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
    756         SkPaint paint;
    757         SkAutoTUnref<SkShader> shader1(SkShader::CreateBitmapShader(bitmap,
    758                 SkShader::TileMode::kClamp_TileMode,
    759                 SkShader::TileMode::kClamp_TileMode));
    760 
    761         SkPoint center;
    762         center.set(50, 50);
    763         SkColor colors[2];
    764         colors[0] = Color::Black;
    765         colors[1] = Color::White;
    766         SkAutoTUnref<SkShader> shader2(SkGradientShader::CreateRadial(center, 50, colors, nullptr, 2,
    767                 SkShader::TileMode::kRepeat_TileMode));
    768 
    769         SkAutoTUnref<SkShader> composeShader(SkShader::CreateComposeShader(shader1, shader2,
    770                 SkXfermode::Mode::kMultiply_Mode));
    771         paint.setShader(composeShader);
    772         canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
    773     });
    774     auto& bitmaps = dl->getBitmapResources();
    775     EXPECT_EQ(1u, bitmaps.size());
    776 }
    777 
    778 TEST(RecordingCanvas, drawText) {
    779     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
    780         Paint paint;
    781         paint.setAntiAlias(true);
    782         paint.setTextSize(20);
    783         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    784         std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO");
    785         canvas.drawText(dst.get(), 0, 5, 5, 25, 25, kBidi_Force_LTR, paint, NULL);
    786     });
    787 
    788     int count = 0;
    789     playbackOps(*dl, [&count](const RecordedOp& op) {
    790         count++;
    791         ASSERT_EQ(RecordedOpId::TextOp, op.opId);
    792         EXPECT_EQ(nullptr, op.localClip);
    793         EXPECT_TRUE(op.localMatrix.isIdentity());
    794         EXPECT_TRUE(op.unmappedBounds.getHeight() >= 10);
    795         EXPECT_TRUE(op.unmappedBounds.getWidth() >= 25);
    796     });
    797     ASSERT_EQ(1, count);
    798 }
    799 
    800 TEST(RecordingCanvas, drawTextInHighContrast) {
    801     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
    802         canvas.setHighContrastText(true);
    803         Paint paint;
    804         paint.setColor(SK_ColorWHITE);
    805         paint.setAntiAlias(true);
    806         paint.setTextSize(20);
    807         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    808         std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO");
    809         canvas.drawText(dst.get(), 0, 5, 5, 25, 25, kBidi_Force_LTR, paint, NULL);
    810     });
    811 
    812     int count = 0;
    813     playbackOps(*dl, [&count](const RecordedOp& op) {
    814         ASSERT_EQ(RecordedOpId::TextOp, op.opId);
    815         if (count++ == 0) {
    816             EXPECT_EQ(SK_ColorBLACK, op.paint->getColor());
    817             EXPECT_EQ(SkPaint::kStrokeAndFill_Style, op.paint->getStyle());
    818         } else {
    819             EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
    820             EXPECT_EQ(SkPaint::kFill_Style, op.paint->getStyle());
    821         }
    822 
    823     });
    824     ASSERT_EQ(2, count);
    825 }
    826 
    827 } // namespace uirenderer
    828 } // namespace android
    829