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