RecordingCanvasTests.cpp revision 1367d2550ebce40f45b16dc651bc3d8d22930801
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 <RecordedOp.h> 20#include <RecordingCanvas.h> 21#include <tests/common/TestUtils.h> 22#include <utils/Color.h> 23 24namespace android { 25namespace uirenderer { 26 27static void playbackOps(const DisplayList& displayList, 28 std::function<void(const RecordedOp&)> opReceiver) { 29 for (auto& chunk : displayList.getChunks()) { 30 for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) { 31 RecordedOp* op = displayList.getOps()[opIndex]; 32 opReceiver(*op); 33 } 34 } 35} 36 37TEST(RecordingCanvas, emptyPlayback) { 38 auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) { 39 canvas.save(SaveFlags::MatrixClip); 40 canvas.restore(); 41 }); 42 playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); }); 43} 44 45TEST(RecordingCanvas, clipRect) { 46 auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) { 47 canvas.save(SaveFlags::MatrixClip); 48 canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op); 49 canvas.drawRect(0, 0, 50, 50, SkPaint()); 50 canvas.drawRect(50, 50, 100, 100, SkPaint()); 51 canvas.restore(); 52 }); 53 54 ASSERT_EQ(2u, dl->getOps().size()) << "Must be exactly two ops"; 55 EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[0]->localClip); 56 EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[1]->localClip); 57 EXPECT_EQ(dl->getOps()[0]->localClip, dl->getOps()[1]->localClip) 58 << "Clip should be serialized once"; 59} 60 61TEST(RecordingCanvas, emptyClipRect) { 62 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { 63 canvas.save(SaveFlags::MatrixClip); 64 canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op); 65 canvas.clipRect(100, 100, 200, 200, SkRegion::kIntersect_Op); 66 canvas.drawRect(0, 0, 50, 50, SkPaint()); // rejected at record time 67 canvas.restore(); 68 }); 69 ASSERT_EQ(0u, dl->getOps().size()) << "Must be zero ops. Rect should be rejected."; 70} 71 72TEST(RecordingCanvas, drawArc) { 73 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { 74 canvas.drawArc(0, 0, 200, 200, 0, 180, true, SkPaint()); 75 canvas.drawArc(0, 0, 100, 100, 0, 360, true, SkPaint()); 76 }); 77 78 auto&& ops = dl->getOps(); 79 ASSERT_EQ(2u, ops.size()) << "Must be exactly two ops"; 80 EXPECT_EQ(RecordedOpId::ArcOp, ops[0]->opId); 81 EXPECT_EQ(Rect(200, 200), ops[0]->unmappedBounds); 82 83 EXPECT_EQ(RecordedOpId::OvalOp, ops[1]->opId) 84 << "Circular arcs should be converted to ovals"; 85 EXPECT_EQ(Rect(100, 100), ops[1]->unmappedBounds); 86} 87 88TEST(RecordingCanvas, drawLines) { 89 auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) { 90 SkPaint paint; 91 paint.setStrokeWidth(20); // doesn't affect recorded bounds - would be resolved at bake time 92 float points[] = { 0, 0, 20, 10, 30, 40, 90 }; // NB: only 1 valid line 93 canvas.drawLines(&points[0], 7, paint); 94 }); 95 96 ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op"; 97 auto op = dl->getOps()[0]; 98 ASSERT_EQ(RecordedOpId::LinesOp, op->opId); 99 EXPECT_EQ(4, ((LinesOp*)op)->floatCount) 100 << "float count must be rounded down to closest multiple of 4"; 101 EXPECT_EQ(Rect(20, 10), op->unmappedBounds) 102 << "unmapped bounds must be size of line, and not outset for stroke width"; 103} 104 105TEST(RecordingCanvas, drawRect) { 106 auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) { 107 canvas.drawRect(10, 20, 90, 180, SkPaint()); 108 }); 109 110 ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op"; 111 auto op = *(dl->getOps()[0]); 112 ASSERT_EQ(RecordedOpId::RectOp, op.opId); 113 EXPECT_EQ(nullptr, op.localClip); 114 EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds); 115} 116 117TEST(RecordingCanvas, drawText) { 118 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { 119 SkPaint paint; 120 paint.setAntiAlias(true); 121 paint.setTextSize(20); 122 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 123 TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25); 124 }); 125 126 int count = 0; 127 playbackOps(*dl, [&count](const RecordedOp& op) { 128 count++; 129 ASSERT_EQ(RecordedOpId::TextOp, op.opId); 130 EXPECT_EQ(nullptr, op.localClip); 131 EXPECT_TRUE(op.localMatrix.isIdentity()); 132 EXPECT_TRUE(op.unmappedBounds.contains(25, 15, 50, 25)) 133 << "Op expected to be 25+ pixels wide, 10+ pixels tall"; 134 }); 135 ASSERT_EQ(1, count); 136} 137 138TEST(RecordingCanvas, drawText_strikeThruAndUnderline) { 139 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { 140 SkPaint paint; 141 paint.setAntiAlias(true); 142 paint.setTextSize(20); 143 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 144 for (int i = 0; i < 2; i++) { 145 for (int j = 0; j < 2; j++) { 146 paint.setUnderlineText(i != 0); 147 paint.setStrikeThruText(j != 0); 148 TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25); 149 } 150 } 151 }); 152 153 auto ops = dl->getOps(); 154 ASSERT_EQ(8u, ops.size()); 155 156 int index = 0; 157 EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); // no underline or strikethrough 158 159 EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); 160 EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough only 161 162 EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); 163 EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline only 164 165 EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); 166 EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline 167 EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough 168} 169 170TEST(RecordingCanvas, drawText_forceAlignLeft) { 171 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { 172 SkPaint paint; 173 paint.setAntiAlias(true); 174 paint.setTextSize(20); 175 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 176 paint.setTextAlign(SkPaint::kLeft_Align); 177 TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25); 178 paint.setTextAlign(SkPaint::kCenter_Align); 179 TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25); 180 paint.setTextAlign(SkPaint::kRight_Align); 181 TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25); 182 }); 183 184 int count = 0; 185 float lastX = FLT_MAX; 186 playbackOps(*dl, [&count, &lastX](const RecordedOp& op) { 187 count++; 188 ASSERT_EQ(RecordedOpId::TextOp, op.opId); 189 EXPECT_EQ(SkPaint::kLeft_Align, op.paint->getTextAlign()) 190 << "recorded drawText commands must force kLeft_Align on their paint"; 191 192 // verify TestUtils alignment offsetting (TODO: move asserts to Canvas base class) 193 EXPECT_GT(lastX, ((const TextOp&)op).x) 194 << "x coordinate should reduce across each of the draw commands, from alignment"; 195 lastX = ((const TextOp&)op).x; 196 }); 197 ASSERT_EQ(3, count); 198} 199 200TEST(RecordingCanvas, drawColor) { 201 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { 202 canvas.drawColor(Color::Black, SkXfermode::kSrcOver_Mode); 203 }); 204 205 ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op"; 206 auto op = *(dl->getOps()[0]); 207 EXPECT_EQ(RecordedOpId::RectOp, op.opId); 208 EXPECT_EQ(nullptr, op.localClip); 209 EXPECT_TRUE(op.unmappedBounds.contains(Rect(-1000, -1000, 1000, 1000))) 210 << "no clip, unmappedBounds should resolve to be much larger than DL bounds"; 211} 212 213TEST(RecordingCanvas, backgroundAndImage) { 214 auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) { 215 SkBitmap bitmap; 216 bitmap.setInfo(SkImageInfo::MakeUnknown(25, 25)); 217 SkPaint paint; 218 paint.setColor(SK_ColorBLUE); 219 220 canvas.save(SaveFlags::MatrixClip); 221 { 222 // a background! 223 canvas.save(SaveFlags::MatrixClip); 224 canvas.drawRect(0, 0, 100, 200, paint); 225 canvas.restore(); 226 } 227 { 228 // an image! 229 canvas.save(SaveFlags::MatrixClip); 230 canvas.translate(25, 25); 231 canvas.scale(2, 2); 232 canvas.drawBitmap(bitmap, 0, 0, nullptr); 233 canvas.restore(); 234 } 235 canvas.restore(); 236 }); 237 238 int count = 0; 239 playbackOps(*dl, [&count](const RecordedOp& op) { 240 if (count == 0) { 241 ASSERT_EQ(RecordedOpId::RectOp, op.opId); 242 ASSERT_NE(nullptr, op.paint); 243 EXPECT_EQ(SK_ColorBLUE, op.paint->getColor()); 244 EXPECT_EQ(Rect(100, 200), op.unmappedBounds); 245 EXPECT_EQ(nullptr, op.localClip); 246 247 Matrix4 expectedMatrix; 248 expectedMatrix.loadIdentity(); 249 EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix); 250 } else { 251 ASSERT_EQ(RecordedOpId::BitmapOp, op.opId); 252 EXPECT_EQ(nullptr, op.paint); 253 EXPECT_EQ(Rect(25, 25), op.unmappedBounds); 254 EXPECT_EQ(nullptr, op.localClip); 255 256 Matrix4 expectedMatrix; 257 expectedMatrix.loadTranslate(25, 25, 0); 258 expectedMatrix.scale(2, 2, 1); 259 EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix); 260 } 261 count++; 262 }); 263 ASSERT_EQ(2, count); 264} 265 266TEST(RecordingCanvas, saveLayer_simple) { 267 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { 268 canvas.saveLayerAlpha(10, 20, 190, 180, 128, SaveFlags::ClipToLayer); 269 canvas.drawRect(10, 20, 190, 180, SkPaint()); 270 canvas.restore(); 271 }); 272 int count = 0; 273 playbackOps(*dl, [&count](const RecordedOp& op) { 274 Matrix4 expectedMatrix; 275 switch(count++) { 276 case 0: 277 EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId); 278 EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds); 279 EXPECT_EQ(nullptr, op.localClip); 280 EXPECT_TRUE(op.localMatrix.isIdentity()); 281 break; 282 case 1: 283 EXPECT_EQ(RecordedOpId::RectOp, op.opId); 284 EXPECT_CLIP_RECT(Rect(180, 160), op.localClip); 285 EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds); 286 expectedMatrix.loadTranslate(-10, -20, 0); 287 EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix); 288 break; 289 case 2: 290 EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId); 291 // Don't bother asserting recording state data - it's not used 292 break; 293 default: 294 ADD_FAILURE(); 295 } 296 }); 297 EXPECT_EQ(3, count); 298} 299 300TEST(RecordingCanvas, saveLayer_missingRestore) { 301 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { 302 canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer); 303 canvas.drawRect(0, 0, 200, 200, SkPaint()); 304 // Note: restore omitted, shouldn't result in unmatched save 305 }); 306 int count = 0; 307 playbackOps(*dl, [&count](const RecordedOp& op) { 308 if (count++ == 2) { 309 EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId); 310 } 311 }); 312 EXPECT_EQ(3, count) << "Missing a restore shouldn't result in an unmatched saveLayer"; 313} 314 315TEST(RecordingCanvas, saveLayer_simpleUnclipped) { 316 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { 317 canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped 318 canvas.drawRect(10, 20, 190, 180, SkPaint()); 319 canvas.restore(); 320 }); 321 int count = 0; 322 playbackOps(*dl, [&count](const RecordedOp& op) { 323 switch(count++) { 324 case 0: 325 EXPECT_EQ(RecordedOpId::BeginUnclippedLayerOp, op.opId); 326 EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds); 327 EXPECT_EQ(nullptr, op.localClip); 328 EXPECT_TRUE(op.localMatrix.isIdentity()); 329 break; 330 case 1: 331 EXPECT_EQ(RecordedOpId::RectOp, op.opId); 332 EXPECT_EQ(nullptr, op.localClip); 333 EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds); 334 EXPECT_TRUE(op.localMatrix.isIdentity()); 335 break; 336 case 2: 337 EXPECT_EQ(RecordedOpId::EndUnclippedLayerOp, op.opId); 338 // Don't bother asserting recording state data - it's not used 339 break; 340 default: 341 ADD_FAILURE(); 342 } 343 }); 344 EXPECT_EQ(3, count); 345} 346 347TEST(RecordingCanvas, saveLayer_addClipFlag) { 348 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { 349 canvas.save(SaveFlags::MatrixClip); 350 canvas.clipRect(10, 20, 190, 180, SkRegion::kIntersect_Op); 351 canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped 352 canvas.drawRect(10, 20, 190, 180, SkPaint()); 353 canvas.restore(); 354 canvas.restore(); 355 }); 356 int count = 0; 357 playbackOps(*dl, [&count](const RecordedOp& op) { 358 if (count++ == 0) { 359 EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId) 360 << "Clip + unclipped saveLayer should result in a clipped layer"; 361 } 362 }); 363 EXPECT_EQ(3, count); 364} 365 366TEST(RecordingCanvas, saveLayer_viewportCrop) { 367 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { 368 // shouldn't matter, since saveLayer will clip to its bounds 369 canvas.clipRect(-1000, -1000, 1000, 1000, SkRegion::kReplace_Op); 370 371 canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer); 372 canvas.drawRect(0, 0, 400, 400, SkPaint()); 373 canvas.restore(); 374 }); 375 int count = 0; 376 playbackOps(*dl, [&count](const RecordedOp& op) { 377 if (count++ == 1) { 378 Matrix4 expectedMatrix; 379 EXPECT_EQ(RecordedOpId::RectOp, op.opId); 380 EXPECT_CLIP_RECT(Rect(100, 100), op.localClip) // Recorded clip rect should be 381 // intersection of viewport and saveLayer bounds, in layer space; 382 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); 383 expectedMatrix.loadTranslate(-100, -100, 0); 384 EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix); 385 } 386 }); 387 EXPECT_EQ(3, count); 388} 389 390TEST(RecordingCanvas, saveLayer_rotateUnclipped) { 391 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { 392 canvas.save(SaveFlags::MatrixClip); 393 canvas.translate(100, 100); 394 canvas.rotate(45); 395 canvas.translate(-50, -50); 396 397 canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer); 398 canvas.drawRect(0, 0, 100, 100, SkPaint()); 399 canvas.restore(); 400 401 canvas.restore(); 402 }); 403 int count = 0; 404 playbackOps(*dl, [&count](const RecordedOp& op) { 405 if (count++ == 1) { 406 EXPECT_EQ(RecordedOpId::RectOp, op.opId); 407 EXPECT_CLIP_RECT(Rect(100, 100), op.localClip); 408 EXPECT_EQ(Rect(100, 100), op.unmappedBounds); 409 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.localMatrix) 410 << "Recorded op shouldn't see any canvas transform before the saveLayer"; 411 } 412 }); 413 EXPECT_EQ(3, count); 414} 415 416TEST(RecordingCanvas, saveLayer_rotateClipped) { 417 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { 418 canvas.save(SaveFlags::MatrixClip); 419 canvas.translate(100, 100); 420 canvas.rotate(45); 421 canvas.translate(-200, -200); 422 423 // area of saveLayer will be clipped to parent viewport, so we ask for 400x400... 424 canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer); 425 canvas.drawRect(0, 0, 400, 400, SkPaint()); 426 canvas.restore(); 427 428 canvas.restore(); 429 }); 430 int count = 0; 431 playbackOps(*dl, [&count](const RecordedOp& op) { 432 if (count++ == 1) { 433 Matrix4 expectedMatrix; 434 EXPECT_EQ(RecordedOpId::RectOp, op.opId); 435 436 // ...and get about 58.6, 58.6, 341.4 341.4, because the bounds are clipped by 437 // the parent 200x200 viewport, but prior to rotation 438 ASSERT_NE(nullptr, op.localClip); 439 ASSERT_EQ(ClipMode::Rectangle, op.localClip->mode); 440 // NOTE: this check relies on saveLayer altering the clip post-viewport init. This 441 // causes the clip to be recorded by contained draw commands, though it's not necessary 442 // since the same clip will be computed at draw time. If such a change is made, this 443 // check could be done at record time by querying the clip, or the clip could be altered 444 // slightly so that it is serialized. 445 EXPECT_EQ(Rect(59, 59, 341, 341), op.localClip->rect); 446 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); 447 expectedMatrix.loadIdentity(); 448 EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix); 449 } 450 }); 451 EXPECT_EQ(3, count); 452} 453 454TEST(RecordingCanvas, drawRenderNode_rejection) { 455 auto child = TestUtils::createNode(50, 50, 150, 150, 456 [](RenderProperties& props, RecordingCanvas& canvas) { 457 SkPaint paint; 458 paint.setColor(SK_ColorWHITE); 459 canvas.drawRect(0, 0, 100, 100, paint); 460 }); 461 462 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&child](RecordingCanvas& canvas) { 463 canvas.clipRect(0, 0, 0, 0, SkRegion::kIntersect_Op); // empty clip, reject node 464 canvas.drawRenderNode(child.get()); // shouldn't crash when rejecting node... 465 }); 466 ASSERT_TRUE(dl->isEmpty()); 467} 468 469TEST(RecordingCanvas, drawRenderNode_projection) { 470 sp<RenderNode> background = TestUtils::createNode(50, 50, 150, 150, 471 [](RenderProperties& props, RecordingCanvas& canvas) { 472 SkPaint paint; 473 paint.setColor(SK_ColorWHITE); 474 canvas.drawRect(0, 0, 100, 100, paint); 475 }); 476 { 477 background->mutateStagingProperties().setProjectionReceiver(false); 478 479 // NO RECEIVER PRESENT 480 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, 481 [&background](RecordingCanvas& canvas) { 482 canvas.drawRect(0, 0, 100, 100, SkPaint()); 483 canvas.drawRenderNode(background.get()); 484 canvas.drawRect(0, 0, 100, 100, SkPaint()); 485 }); 486 EXPECT_EQ(-1, dl->projectionReceiveIndex) 487 << "no projection receiver should have been observed"; 488 } 489 { 490 background->mutateStagingProperties().setProjectionReceiver(true); 491 492 // RECEIVER PRESENT 493 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, 494 [&background](RecordingCanvas& canvas) { 495 canvas.drawRect(0, 0, 100, 100, SkPaint()); 496 canvas.drawRenderNode(background.get()); 497 canvas.drawRect(0, 0, 100, 100, SkPaint()); 498 }); 499 500 ASSERT_EQ(3u, dl->getOps().size()) << "Must be three ops"; 501 auto op = dl->getOps()[1]; 502 EXPECT_EQ(RecordedOpId::RenderNodeOp, op->opId); 503 EXPECT_EQ(1, dl->projectionReceiveIndex) 504 << "correct projection receiver not identified"; 505 506 // verify the behavior works even though projection receiver hasn't been sync'd yet 507 EXPECT_TRUE(background->stagingProperties().isProjectionReceiver()); 508 EXPECT_FALSE(background->properties().isProjectionReceiver()); 509 } 510} 511 512TEST(RecordingCanvas, firstClipWillReplace) { 513 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { 514 canvas.save(SaveFlags::MatrixClip); 515 // since no explicit clip set on canvas, this should be the one observed on op: 516 canvas.clipRect(-100, -100, 300, 300, SkRegion::kIntersect_Op); 517 518 SkPaint paint; 519 paint.setColor(SK_ColorWHITE); 520 canvas.drawRect(0, 0, 100, 100, paint); 521 522 canvas.restore(); 523 }); 524 ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op"; 525 // first clip must be preserved, even if it extends beyond canvas bounds 526 EXPECT_CLIP_RECT(Rect(-100, -100, 300, 300), dl->getOps()[0]->localClip); 527} 528 529TEST(RecordingCanvas, insertReorderBarrier) { 530 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { 531 canvas.drawRect(0, 0, 400, 400, SkPaint()); 532 canvas.insertReorderBarrier(true); 533 canvas.insertReorderBarrier(false); 534 canvas.insertReorderBarrier(false); 535 canvas.insertReorderBarrier(true); 536 canvas.drawRect(0, 0, 400, 400, SkPaint()); 537 canvas.insertReorderBarrier(false); 538 }); 539 540 auto chunks = dl->getChunks(); 541 EXPECT_EQ(0u, chunks[0].beginOpIndex); 542 EXPECT_EQ(1u, chunks[0].endOpIndex); 543 EXPECT_FALSE(chunks[0].reorderChildren); 544 545 EXPECT_EQ(1u, chunks[1].beginOpIndex); 546 EXPECT_EQ(2u, chunks[1].endOpIndex); 547 EXPECT_TRUE(chunks[1].reorderChildren); 548} 549 550TEST(RecordingCanvas, refPaint) { 551 SkPaint paint; 552 paint.setAntiAlias(true); 553 paint.setTextSize(20); 554 paint.setTextAlign(SkPaint::kLeft_Align); 555 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 556 557 auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&paint](RecordingCanvas& canvas) { 558 paint.setColor(SK_ColorBLUE); 559 // first three should use same paint 560 canvas.drawRect(0, 0, 200, 10, paint); 561 SkPaint paintCopy(paint); 562 canvas.drawRect(0, 10, 200, 20, paintCopy); 563 TestUtils::drawTextToCanvas(&canvas, "helloworld", paint, 50, 25); 564 565 // only here do we use different paint ptr 566 paint.setColor(SK_ColorRED); 567 canvas.drawRect(0, 20, 200, 30, paint); 568 }); 569 auto ops = dl->getOps(); 570 ASSERT_EQ(4u, ops.size()); 571 572 // first three are the same 573 EXPECT_NE(nullptr, ops[0]->paint); 574 EXPECT_NE(&paint, ops[0]->paint); 575 EXPECT_EQ(ops[0]->paint, ops[1]->paint); 576 EXPECT_EQ(ops[0]->paint, ops[2]->paint); 577 578 // last is different, but still copied / non-null 579 EXPECT_NE(nullptr, ops[3]->paint); 580 EXPECT_NE(ops[0]->paint, ops[3]->paint); 581 EXPECT_NE(&paint, ops[3]->paint); 582} 583 584} // namespace uirenderer 585} // namespace android 586