RecordingCanvas.cpp revision 1d8e194661085f9a18ab1b3cd12f9e19d3a86be5
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 "RecordingCanvas.h" 18 19#include "DeferredLayerUpdater.h" 20#include "RecordedOp.h" 21#include "RenderNode.h" 22#include "VectorDrawable.h" 23 24namespace android { 25namespace uirenderer { 26 27RecordingCanvas::RecordingCanvas(size_t width, size_t height) 28 : mState(*this) 29 , mResourceCache(ResourceCache::getInstance()) { 30 resetRecording(width, height); 31} 32 33RecordingCanvas::~RecordingCanvas() { 34 LOG_ALWAYS_FATAL_IF(mDisplayList, 35 "Destroyed a RecordingCanvas during a record!"); 36} 37 38void RecordingCanvas::resetRecording(int width, int height) { 39 LOG_ALWAYS_FATAL_IF(mDisplayList, 40 "prepareDirty called a second time during a recording!"); 41 mDisplayList = new DisplayList(); 42 43 mState.initializeRecordingSaveStack(width, height); 44 45 mDeferredBarrierType = DeferredBarrierType::InOrder; 46 mState.setDirtyClip(false); 47} 48 49DisplayList* RecordingCanvas::finishRecording() { 50 restoreToCount(1); 51 mPaintMap.clear(); 52 mRegionMap.clear(); 53 mPathMap.clear(); 54 DisplayList* displayList = mDisplayList; 55 mDisplayList = nullptr; 56 mSkiaCanvasProxy.reset(nullptr); 57 return displayList; 58} 59 60SkCanvas* RecordingCanvas::asSkCanvas() { 61 LOG_ALWAYS_FATAL_IF(!mDisplayList, 62 "attempting to get an SkCanvas when we are not recording!"); 63 if (!mSkiaCanvasProxy) { 64 mSkiaCanvasProxy.reset(new SkiaCanvasProxy(this)); 65 } 66 67 // SkCanvas instances default to identity transform, but should inherit 68 // the state of this Canvas; if this code was in the SkiaCanvasProxy 69 // constructor, we couldn't cache mSkiaCanvasProxy. 70 SkMatrix parentTransform; 71 getMatrix(&parentTransform); 72 mSkiaCanvasProxy.get()->setMatrix(parentTransform); 73 74 return mSkiaCanvasProxy.get(); 75} 76 77// ---------------------------------------------------------------------------- 78// CanvasStateClient implementation 79// ---------------------------------------------------------------------------- 80 81void RecordingCanvas::onViewportInitialized() { 82} 83 84void RecordingCanvas::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) { 85 if (removed.flags & Snapshot::kFlagIsFboLayer) { 86 addOp(alloc().create_trivial<EndLayerOp>()); 87 } else if (removed.flags & Snapshot::kFlagIsLayer) { 88 addOp(alloc().create_trivial<EndUnclippedLayerOp>()); 89 } 90} 91 92// ---------------------------------------------------------------------------- 93// android/graphics/Canvas state operations 94// ---------------------------------------------------------------------------- 95// Save (layer) 96int RecordingCanvas::save(SaveFlags::Flags flags) { 97 return mState.save((int) flags); 98} 99 100void RecordingCanvas::RecordingCanvas::restore() { 101 mState.restore(); 102} 103 104void RecordingCanvas::restoreToCount(int saveCount) { 105 mState.restoreToCount(saveCount); 106} 107 108int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, 109 const SkPaint* paint, SaveFlags::Flags flags) { 110 // force matrix/clip isolation for layer 111 flags |= SaveFlags::MatrixClip; 112 bool clippedLayer = flags & SaveFlags::ClipToLayer; 113 114 const Snapshot& previous = *mState.currentSnapshot(); 115 116 // initialize the snapshot as though it almost represents an FBO layer so deferred draw 117 // operations will be able to store and restore the current clip and transform info, and 118 // quick rejection will be correct (for display lists) 119 120 const Rect unmappedBounds(left, top, right, bottom); 121 122 // determine clipped bounds relative to previous viewport. 123 Rect visibleBounds = unmappedBounds; 124 previous.transform->mapRect(visibleBounds); 125 126 if (CC_UNLIKELY(!clippedLayer 127 && previous.transform->rectToRect() 128 && visibleBounds.contains(previous.getRenderTargetClip()))) { 129 // unlikely case where an unclipped savelayer is recorded with a clip it can use, 130 // as none of its unaffected/unclipped area is visible 131 clippedLayer = true; 132 flags |= SaveFlags::ClipToLayer; 133 } 134 135 visibleBounds.doIntersect(previous.getRenderTargetClip()); 136 visibleBounds.snapToPixelBoundaries(); 137 visibleBounds.doIntersect(Rect(previous.getViewportWidth(), previous.getViewportHeight())); 138 139 // Map visible bounds back to layer space, and intersect with parameter bounds 140 Rect layerBounds = visibleBounds; 141 Matrix4 inverse; 142 inverse.loadInverse(*previous.transform); 143 inverse.mapRect(layerBounds); 144 layerBounds.doIntersect(unmappedBounds); 145 146 int saveValue = mState.save((int) flags); 147 Snapshot& snapshot = *mState.writableSnapshot(); 148 149 // layerBounds is in original bounds space, but clipped by current recording clip 150 if (layerBounds.isEmpty() || unmappedBounds.isEmpty()) { 151 // Don't bother recording layer, since it's been rejected 152 if (CC_LIKELY(clippedLayer)) { 153 snapshot.resetClip(0, 0, 0, 0); 154 } 155 return saveValue; 156 } 157 158 if (CC_LIKELY(clippedLayer)) { 159 auto previousClip = getRecordedClip(); // note: done before new snapshot's clip has changed 160 161 snapshot.flags |= Snapshot::kFlagIsLayer | Snapshot::kFlagIsFboLayer; 162 snapshot.initializeViewport(unmappedBounds.getWidth(), unmappedBounds.getHeight()); 163 snapshot.transform->loadTranslate(-unmappedBounds.left, -unmappedBounds.top, 0.0f); 164 165 Rect clip = layerBounds; 166 clip.translate(-unmappedBounds.left, -unmappedBounds.top); 167 snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom); 168 snapshot.roundRectClipState = nullptr; 169 170 addOp(alloc().create_trivial<BeginLayerOp>( 171 unmappedBounds, 172 *previous.transform, // transform to *draw* with 173 previousClip, // clip to *draw* with 174 refPaint(paint))); 175 } else { 176 snapshot.flags |= Snapshot::kFlagIsLayer; 177 178 addOp(alloc().create_trivial<BeginUnclippedLayerOp>( 179 unmappedBounds, 180 *mState.currentSnapshot()->transform, 181 getRecordedClip(), 182 refPaint(paint))); 183 } 184 185 return saveValue; 186} 187 188// Matrix 189void RecordingCanvas::rotate(float degrees) { 190 if (degrees == 0) return; 191 192 mState.rotate(degrees); 193} 194 195void RecordingCanvas::scale(float sx, float sy) { 196 if (sx == 1 && sy == 1) return; 197 198 mState.scale(sx, sy); 199} 200 201void RecordingCanvas::skew(float sx, float sy) { 202 mState.skew(sx, sy); 203} 204 205void RecordingCanvas::translate(float dx, float dy) { 206 if (dx == 0 && dy == 0) return; 207 208 mState.translate(dx, dy, 0); 209} 210 211// Clip 212bool RecordingCanvas::getClipBounds(SkRect* outRect) const { 213 *outRect = mState.getLocalClipBounds().toSkRect(); 214 return !(outRect->isEmpty()); 215} 216bool RecordingCanvas::quickRejectRect(float left, float top, float right, float bottom) const { 217 return mState.quickRejectConservative(left, top, right, bottom); 218} 219bool RecordingCanvas::quickRejectPath(const SkPath& path) const { 220 SkRect bounds = path.getBounds(); 221 return mState.quickRejectConservative(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom); 222} 223bool RecordingCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { 224 return mState.clipRect(left, top, right, bottom, op); 225} 226bool RecordingCanvas::clipPath(const SkPath* path, SkRegion::Op op) { 227 return mState.clipPath(path, op); 228} 229bool RecordingCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) { 230 return mState.clipRegion(region, op); 231} 232 233// ---------------------------------------------------------------------------- 234// android/graphics/Canvas draw operations 235// ---------------------------------------------------------------------------- 236void RecordingCanvas::drawColor(int color, SkXfermode::Mode mode) { 237 addOp(alloc().create_trivial<ColorOp>( 238 getRecordedClip(), 239 color, 240 mode)); 241} 242 243void RecordingCanvas::drawPaint(const SkPaint& paint) { 244 SkRect bounds; 245 if (getClipBounds(&bounds)) { 246 drawRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, paint); 247 } 248} 249 250static Rect calcBoundsOfPoints(const float* points, int floatCount) { 251 Rect unmappedBounds(points[0], points[1], points[0], points[1]); 252 for (int i = 2; i < floatCount; i += 2) { 253 unmappedBounds.expandToCover(points[i], points[i + 1]); 254 } 255 return unmappedBounds; 256} 257 258// Geometry 259void RecordingCanvas::drawPoints(const float* points, int floatCount, const SkPaint& paint) { 260 if (floatCount < 2) return; 261 floatCount &= ~0x1; // round down to nearest two 262 263 addOp(alloc().create_trivial<PointsOp>( 264 calcBoundsOfPoints(points, floatCount), 265 *mState.currentSnapshot()->transform, 266 getRecordedClip(), 267 refPaint(&paint), refBuffer<float>(points, floatCount), floatCount)); 268} 269 270void RecordingCanvas::drawLines(const float* points, int floatCount, const SkPaint& paint) { 271 if (floatCount < 4) return; 272 floatCount &= ~0x3; // round down to nearest four 273 274 addOp(alloc().create_trivial<LinesOp>( 275 calcBoundsOfPoints(points, floatCount), 276 *mState.currentSnapshot()->transform, 277 getRecordedClip(), 278 refPaint(&paint), refBuffer<float>(points, floatCount), floatCount)); 279} 280 281void RecordingCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) { 282 addOp(alloc().create_trivial<RectOp>( 283 Rect(left, top, right, bottom), 284 *(mState.currentSnapshot()->transform), 285 getRecordedClip(), 286 refPaint(&paint))); 287} 288 289void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint) { 290 if (rects == nullptr) return; 291 292 Vertex* rectData = (Vertex*) mDisplayList->allocator.create_trivial_array<Vertex>(vertexCount); 293 Vertex* vertex = rectData; 294 295 float left = FLT_MAX; 296 float top = FLT_MAX; 297 float right = FLT_MIN; 298 float bottom = FLT_MIN; 299 for (int index = 0; index < vertexCount; index += 4) { 300 float l = rects[index + 0]; 301 float t = rects[index + 1]; 302 float r = rects[index + 2]; 303 float b = rects[index + 3]; 304 305 Vertex::set(vertex++, l, t); 306 Vertex::set(vertex++, r, t); 307 Vertex::set(vertex++, l, b); 308 Vertex::set(vertex++, r, b); 309 310 left = std::min(left, l); 311 top = std::min(top, t); 312 right = std::max(right, r); 313 bottom = std::max(bottom, b); 314 } 315 addOp(alloc().create_trivial<SimpleRectsOp>( 316 Rect(left, top, right, bottom), 317 *(mState.currentSnapshot()->transform), 318 getRecordedClip(), 319 refPaint(paint), rectData, vertexCount)); 320} 321 322void RecordingCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) { 323 if (paint.getStyle() == SkPaint::kFill_Style 324 && (!paint.isAntiAlias() || mState.currentTransform()->isSimple())) { 325 int count = 0; 326 Vector<float> rects; 327 SkRegion::Iterator it(region); 328 while (!it.done()) { 329 const SkIRect& r = it.rect(); 330 rects.push(r.fLeft); 331 rects.push(r.fTop); 332 rects.push(r.fRight); 333 rects.push(r.fBottom); 334 count += 4; 335 it.next(); 336 } 337 drawSimpleRects(rects.array(), count, &paint); 338 } else { 339 SkRegion::Iterator it(region); 340 while (!it.done()) { 341 const SkIRect& r = it.rect(); 342 drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint); 343 it.next(); 344 } 345 } 346} 347void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom, 348 float rx, float ry, const SkPaint& paint) { 349 if (CC_LIKELY(MathUtils::isPositive(rx) || MathUtils::isPositive(ry))) { 350 addOp(alloc().create_trivial<RoundRectOp>( 351 Rect(left, top, right, bottom), 352 *(mState.currentSnapshot()->transform), 353 getRecordedClip(), 354 refPaint(&paint), rx, ry)); 355 } else { 356 drawRect(left, top, right, bottom, paint); 357 } 358} 359 360void RecordingCanvas::drawRoundRect( 361 CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top, 362 CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom, 363 CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry, 364 CanvasPropertyPaint* paint) { 365 mDisplayList->ref(left); 366 mDisplayList->ref(top); 367 mDisplayList->ref(right); 368 mDisplayList->ref(bottom); 369 mDisplayList->ref(rx); 370 mDisplayList->ref(ry); 371 mDisplayList->ref(paint); 372 refBitmapsInShader(paint->value.getShader()); 373 addOp(alloc().create_trivial<RoundRectPropsOp>( 374 *(mState.currentSnapshot()->transform), 375 getRecordedClip(), 376 &paint->value, 377 &left->value, &top->value, &right->value, &bottom->value, 378 &rx->value, &ry->value)); 379} 380 381void RecordingCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) { 382 // TODO: move to Canvas.h 383 if (radius <= 0) return; 384 drawOval(x - radius, y - radius, x + radius, y + radius, paint); 385} 386 387void RecordingCanvas::drawCircle( 388 CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y, 389 CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) { 390 mDisplayList->ref(x); 391 mDisplayList->ref(y); 392 mDisplayList->ref(radius); 393 mDisplayList->ref(paint); 394 refBitmapsInShader(paint->value.getShader()); 395 addOp(alloc().create_trivial<CirclePropsOp>( 396 *(mState.currentSnapshot()->transform), 397 getRecordedClip(), 398 &paint->value, 399 &x->value, &y->value, &radius->value)); 400} 401 402void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) { 403 addOp(alloc().create_trivial<OvalOp>( 404 Rect(left, top, right, bottom), 405 *(mState.currentSnapshot()->transform), 406 getRecordedClip(), 407 refPaint(&paint))); 408} 409 410void RecordingCanvas::drawArc(float left, float top, float right, float bottom, 411 float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) { 412 if (fabs(sweepAngle) >= 360.0f) { 413 drawOval(left, top, right, bottom, paint); 414 } else { 415 addOp(alloc().create_trivial<ArcOp>( 416 Rect(left, top, right, bottom), 417 *(mState.currentSnapshot()->transform), 418 getRecordedClip(), 419 refPaint(&paint), 420 startAngle, sweepAngle, useCenter)); 421 } 422} 423 424void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) { 425 addOp(alloc().create_trivial<PathOp>( 426 Rect(path.getBounds()), 427 *(mState.currentSnapshot()->transform), 428 getRecordedClip(), 429 refPaint(&paint), refPath(&path))); 430} 431 432void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { 433 mDisplayList->pushStagingFunctors.push_back(tree->getFunctor()); 434 mDisplayList->ref(tree); 435 addOp(alloc().create_trivial<VectorDrawableOp>( 436 tree, 437 Rect(tree->stagingProperties()->getBounds()), 438 *(mState.currentSnapshot()->transform), 439 getRecordedClip())); 440} 441 442// Bitmap-based 443void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) { 444 save(SaveFlags::Matrix); 445 translate(left, top); 446 drawBitmap(&bitmap, paint); 447 restore(); 448} 449 450void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, 451 const SkPaint* paint) { 452 if (matrix.isIdentity()) { 453 drawBitmap(&bitmap, paint); 454 } else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) 455 && MathUtils::isPositive(matrix.getScaleX()) 456 && MathUtils::isPositive(matrix.getScaleY())) { 457 // SkMatrix::isScaleTranslate() not available in L 458 SkRect src; 459 SkRect dst; 460 bitmap.getBounds(&src); 461 matrix.mapRect(&dst, src); 462 drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, 463 dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint); 464 } else { 465 save(SaveFlags::Matrix); 466 concat(matrix); 467 drawBitmap(&bitmap, paint); 468 restore(); 469 } 470} 471 472void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop, 473 float srcRight, float srcBottom, float dstLeft, float dstTop, 474 float dstRight, float dstBottom, const SkPaint* paint) { 475 if (srcLeft == 0 && srcTop == 0 476 && srcRight == bitmap.width() 477 && srcBottom == bitmap.height() 478 && (srcBottom - srcTop == dstBottom - dstTop) 479 && (srcRight - srcLeft == dstRight - dstLeft)) { 480 // transform simple rect to rect drawing case into position bitmap ops, since they merge 481 save(SaveFlags::Matrix); 482 translate(dstLeft, dstTop); 483 drawBitmap(&bitmap, paint); 484 restore(); 485 } else { 486 addOp(alloc().create_trivial<BitmapRectOp>( 487 Rect(dstLeft, dstTop, dstRight, dstBottom), 488 *(mState.currentSnapshot()->transform), 489 getRecordedClip(), 490 refPaint(paint), refBitmap(bitmap), 491 Rect(srcLeft, srcTop, srcRight, srcBottom))); 492 } 493} 494 495void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight, 496 const float* vertices, const int* colors, const SkPaint* paint) { 497 int vertexCount = (meshWidth + 1) * (meshHeight + 1); 498 addOp(alloc().create_trivial<BitmapMeshOp>( 499 calcBoundsOfPoints(vertices, vertexCount * 2), 500 *(mState.currentSnapshot()->transform), 501 getRecordedClip(), 502 refPaint(paint), refBitmap(bitmap), meshWidth, meshHeight, 503 refBuffer<float>(vertices, vertexCount * 2), // 2 floats per vertex 504 refBuffer<int>(colors, vertexCount))); // 1 color per vertex 505} 506 507void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& patch, 508 float dstLeft, float dstTop, float dstRight, float dstBottom, 509 const SkPaint* paint) { 510 addOp(alloc().create_trivial<PatchOp>( 511 Rect(dstLeft, dstTop, dstRight, dstBottom), 512 *(mState.currentSnapshot()->transform), 513 getRecordedClip(), 514 refPaint(paint), refBitmap(bitmap), refPatch(&patch))); 515} 516 517// Text 518void RecordingCanvas::drawGlyphs(const uint16_t* glyphs, const float* positions, int glyphCount, 519 const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, 520 float boundsRight, float boundsBottom, float totalAdvance) { 521 if (!glyphs || !positions || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return; 522 glyphs = refBuffer<glyph_t>(glyphs, glyphCount); 523 positions = refBuffer<float>(positions, glyphCount * 2); 524 525 // TODO: either must account for text shadow in bounds, or record separate ops for text shadows 526 addOp(alloc().create_trivial<TextOp>( 527 Rect(boundsLeft, boundsTop, boundsRight, boundsBottom), 528 *(mState.currentSnapshot()->transform), 529 getRecordedClip(), 530 refPaint(&paint), glyphs, positions, glyphCount, x, y)); 531 drawTextDecorations(x, y, totalAdvance, paint); 532} 533 534void RecordingCanvas::drawGlyphsOnPath(const uint16_t* glyphs, int glyphCount, const SkPath& path, 535 float hOffset, float vOffset, const SkPaint& paint) { 536 if (!glyphs || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return; 537 glyphs = refBuffer<glyph_t>(glyphs, glyphCount); 538 addOp(alloc().create_trivial<TextOnPathOp>( 539 *(mState.currentSnapshot()->transform), 540 getRecordedClip(), 541 refPaint(&paint), glyphs, glyphCount, refPath(&path), hOffset, vOffset)); 542} 543 544void RecordingCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) { 545 addOp(alloc().create_trivial<BitmapOp>( 546 Rect(bitmap->width(), bitmap->height()), 547 *(mState.currentSnapshot()->transform), 548 getRecordedClip(), 549 refPaint(paint), refBitmap(*bitmap))); 550} 551 552void RecordingCanvas::drawRenderNode(RenderNode* renderNode) { 553 auto&& stagingProps = renderNode->stagingProperties(); 554 RenderNodeOp* op = alloc().create_trivial<RenderNodeOp>( 555 Rect(stagingProps.getWidth(), stagingProps.getHeight()), 556 *(mState.currentSnapshot()->transform), 557 getRecordedClip(), 558 renderNode); 559 int opIndex = addOp(op); 560 if (CC_LIKELY(opIndex >= 0)) { 561 int childIndex = mDisplayList->addChild(op); 562 563 // update the chunk's child indices 564 DisplayList::Chunk& chunk = mDisplayList->chunks.back(); 565 chunk.endChildIndex = childIndex + 1; 566 567 if (renderNode->stagingProperties().isProjectionReceiver()) { 568 // use staging property, since recording on UI thread 569 mDisplayList->projectionReceiveIndex = opIndex; 570 } 571 } 572} 573 574void RecordingCanvas::drawLayer(DeferredLayerUpdater* layerHandle) { 575 // We ref the DeferredLayerUpdater due to its thread-safe ref-counting semantics. 576 mDisplayList->ref(layerHandle); 577 578 // Note that the backing layer has *not* yet been updated, so don't trust 579 // its width, height, transform, etc...! 580 addOp(alloc().create_trivial<TextureLayerOp>( 581 Rect(layerHandle->getWidth(), layerHandle->getHeight()), 582 *(mState.currentSnapshot()->transform), 583 getRecordedClip(), 584 layerHandle->backingLayer())); 585} 586 587void RecordingCanvas::callDrawGLFunction(Functor* functor) { 588 mDisplayList->functors.push_back(functor); 589 addOp(alloc().create_trivial<FunctorOp>( 590 *(mState.currentSnapshot()->transform), 591 getRecordedClip(), 592 functor)); 593} 594 595size_t RecordingCanvas::addOp(RecordedOp* op) { 596 // skip op with empty clip 597 if (op->localClip && op->localClip->rect.isEmpty()) { 598 // NOTE: this rejection happens after op construction/content ref-ing, so content ref'd 599 // and held by renderthread isn't affected by clip rejection. 600 // Could rewind alloc here if desired, but callers would have to not touch op afterwards. 601 return -1; 602 } 603 604 int insertIndex = mDisplayList->ops.size(); 605 mDisplayList->ops.push_back(op); 606 if (mDeferredBarrierType != DeferredBarrierType::None) { 607 // op is first in new chunk 608 mDisplayList->chunks.emplace_back(); 609 DisplayList::Chunk& newChunk = mDisplayList->chunks.back(); 610 newChunk.beginOpIndex = insertIndex; 611 newChunk.endOpIndex = insertIndex + 1; 612 newChunk.reorderChildren = (mDeferredBarrierType == DeferredBarrierType::OutOfOrder); 613 614 int nextChildIndex = mDisplayList->children.size(); 615 newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex; 616 mDeferredBarrierType = DeferredBarrierType::None; 617 } else { 618 // standard case - append to existing chunk 619 mDisplayList->chunks.back().endOpIndex = insertIndex + 1; 620 } 621 return insertIndex; 622} 623 624void RecordingCanvas::refBitmapsInShader(const SkShader* shader) { 625 if (!shader) return; 626 627 // If this paint has an SkShader that has an SkBitmap add 628 // it to the bitmap pile 629 SkBitmap bitmap; 630 SkShader::TileMode xy[2]; 631 if (shader->isABitmap(&bitmap, nullptr, xy)) { 632 refBitmap(bitmap); 633 return; 634 } 635 SkShader::ComposeRec rec; 636 if (shader->asACompose(&rec)) { 637 refBitmapsInShader(rec.fShaderA); 638 refBitmapsInShader(rec.fShaderB); 639 return; 640 } 641} 642 643}; // namespace uirenderer 644}; // namespace android 645