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