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