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