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