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