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