RecordingCanvas.cpp revision 8cd3edfa15cc9cdbffa935d19ab894426b08d174
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, RenderNode* node) { 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 if (CC_LIKELY(!layerBounds.isEmpty())) { 153 // if non-empty, can safely map by the inverse transform 154 Matrix4 inverse; 155 inverse.loadInverse(*previous.transform); 156 inverse.mapRect(layerBounds); 157 layerBounds.doIntersect(unmappedBounds); 158 } 159 160 int saveValue = mState.save((int) flags); 161 Snapshot& snapshot = *mState.writableSnapshot(); 162 163 // layerBounds is in original bounds space, but clipped by current recording clip 164 if (!layerBounds.isEmpty() && !unmappedBounds.isEmpty()) { 165 if (CC_LIKELY(clippedLayer)) { 166 auto previousClip = getRecordedClip(); // capture before new snapshot clip has changed 167 if (addOp(alloc().create_trivial<BeginLayerOp>( 168 unmappedBounds, 169 *previous.transform, // transform to *draw* with 170 previousClip, // clip to *draw* with 171 refPaint(paint))) >= 0) { 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 return saveValue; 181 } 182 } else { 183 if (addOp(alloc().create_trivial<BeginUnclippedLayerOp>( 184 unmappedBounds, 185 *mState.currentSnapshot()->transform, 186 getRecordedClip(), 187 refPaint(paint))) >= 0) { 188 snapshot.flags |= Snapshot::kFlagIsLayer; 189 return saveValue; 190 } 191 } 192 } 193 194 // Layer not needed, so skip recording it... 195 if (CC_LIKELY(clippedLayer)) { 196 // ... and set empty clip to reject inner content, if possible 197 snapshot.resetClip(0, 0, 0, 0); 198 } 199 return saveValue; 200} 201 202// Matrix 203void RecordingCanvas::rotate(float degrees) { 204 if (degrees == 0) return; 205 206 mState.rotate(degrees); 207} 208 209void RecordingCanvas::scale(float sx, float sy) { 210 if (sx == 1 && sy == 1) return; 211 212 mState.scale(sx, sy); 213} 214 215void RecordingCanvas::skew(float sx, float sy) { 216 mState.skew(sx, sy); 217} 218 219void RecordingCanvas::translate(float dx, float dy) { 220 if (dx == 0 && dy == 0) return; 221 222 mState.translate(dx, dy, 0); 223} 224 225// Clip 226bool RecordingCanvas::getClipBounds(SkRect* outRect) const { 227 *outRect = mState.getLocalClipBounds().toSkRect(); 228 return !(outRect->isEmpty()); 229} 230bool RecordingCanvas::quickRejectRect(float left, float top, float right, float bottom) const { 231 return mState.quickRejectConservative(left, top, right, bottom); 232} 233bool RecordingCanvas::quickRejectPath(const SkPath& path) const { 234 SkRect bounds = path.getBounds(); 235 return mState.quickRejectConservative(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom); 236} 237bool RecordingCanvas::clipRect(float left, float top, float right, float bottom, SkClipOp op) { 238 return mState.clipRect(left, top, right, bottom, op); 239} 240bool RecordingCanvas::clipPath(const SkPath* path, SkClipOp op) { 241 return mState.clipPath(path, op); 242} 243 244// ---------------------------------------------------------------------------- 245// android/graphics/Canvas draw operations 246// ---------------------------------------------------------------------------- 247void RecordingCanvas::drawColor(int color, SkBlendMode 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 (CC_UNLIKELY(floatCount < 2 || PaintUtils::paintWillNotDraw(paint))) 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 (CC_UNLIKELY(floatCount < 4 || PaintUtils::paintWillNotDraw(paint))) 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 if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return; 294 295 addOp(alloc().create_trivial<RectOp>( 296 Rect(left, top, right, bottom), 297 *(mState.currentSnapshot()->transform), 298 getRecordedClip(), 299 refPaint(&paint))); 300} 301 302void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint) { 303 if (rects == nullptr) return; 304 305 Vertex* rectData = (Vertex*) mDisplayList->allocator.create_trivial_array<Vertex>(vertexCount); 306 Vertex* vertex = rectData; 307 308 float left = FLT_MAX; 309 float top = FLT_MAX; 310 float right = FLT_MIN; 311 float bottom = FLT_MIN; 312 for (int index = 0; index < vertexCount; index += 4) { 313 float l = rects[index + 0]; 314 float t = rects[index + 1]; 315 float r = rects[index + 2]; 316 float b = rects[index + 3]; 317 318 Vertex::set(vertex++, l, t); 319 Vertex::set(vertex++, r, t); 320 Vertex::set(vertex++, l, b); 321 Vertex::set(vertex++, r, b); 322 323 left = std::min(left, l); 324 top = std::min(top, t); 325 right = std::max(right, r); 326 bottom = std::max(bottom, b); 327 } 328 addOp(alloc().create_trivial<SimpleRectsOp>( 329 Rect(left, top, right, bottom), 330 *(mState.currentSnapshot()->transform), 331 getRecordedClip(), 332 refPaint(paint), rectData, vertexCount)); 333} 334 335void RecordingCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) { 336 if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return; 337 338 if (paint.getStyle() == SkPaint::kFill_Style 339 && (!paint.isAntiAlias() || mState.currentTransform()->isSimple())) { 340 int count = 0; 341 Vector<float> rects; 342 SkRegion::Iterator it(region); 343 while (!it.done()) { 344 const SkIRect& r = it.rect(); 345 rects.push(r.fLeft); 346 rects.push(r.fTop); 347 rects.push(r.fRight); 348 rects.push(r.fBottom); 349 count += 4; 350 it.next(); 351 } 352 drawSimpleRects(rects.array(), count, &paint); 353 } else { 354 SkRegion::Iterator it(region); 355 while (!it.done()) { 356 const SkIRect& r = it.rect(); 357 drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint); 358 it.next(); 359 } 360 } 361} 362 363void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom, 364 float rx, float ry, const SkPaint& paint) { 365 if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return; 366 367 if (CC_LIKELY(MathUtils::isPositive(rx) || MathUtils::isPositive(ry))) { 368 addOp(alloc().create_trivial<RoundRectOp>( 369 Rect(left, top, right, bottom), 370 *(mState.currentSnapshot()->transform), 371 getRecordedClip(), 372 refPaint(&paint), rx, ry)); 373 } else { 374 drawRect(left, top, right, bottom, paint); 375 } 376} 377 378void RecordingCanvas::drawRoundRect( 379 CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top, 380 CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom, 381 CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry, 382 CanvasPropertyPaint* paint) { 383 mDisplayList->ref(left); 384 mDisplayList->ref(top); 385 mDisplayList->ref(right); 386 mDisplayList->ref(bottom); 387 mDisplayList->ref(rx); 388 mDisplayList->ref(ry); 389 mDisplayList->ref(paint); 390 refBitmapsInShader(paint->value.getShader()); 391 addOp(alloc().create_trivial<RoundRectPropsOp>( 392 *(mState.currentSnapshot()->transform), 393 getRecordedClip(), 394 &paint->value, 395 &left->value, &top->value, &right->value, &bottom->value, 396 &rx->value, &ry->value)); 397} 398 399void RecordingCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) { 400 // TODO: move to Canvas.h 401 if (CC_UNLIKELY(radius <= 0 || PaintUtils::paintWillNotDraw(paint))) return; 402 403 drawOval(x - radius, y - radius, x + radius, y + radius, paint); 404} 405 406void RecordingCanvas::drawCircle( 407 CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y, 408 CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) { 409 mDisplayList->ref(x); 410 mDisplayList->ref(y); 411 mDisplayList->ref(radius); 412 mDisplayList->ref(paint); 413 refBitmapsInShader(paint->value.getShader()); 414 addOp(alloc().create_trivial<CirclePropsOp>( 415 *(mState.currentSnapshot()->transform), 416 getRecordedClip(), 417 &paint->value, 418 &x->value, &y->value, &radius->value)); 419} 420 421void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) { 422 if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return; 423 424 addOp(alloc().create_trivial<OvalOp>( 425 Rect(left, top, right, bottom), 426 *(mState.currentSnapshot()->transform), 427 getRecordedClip(), 428 refPaint(&paint))); 429} 430 431void RecordingCanvas::drawArc(float left, float top, float right, float bottom, 432 float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) { 433 if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return; 434 435 if (fabs(sweepAngle) >= 360.0f) { 436 drawOval(left, top, right, bottom, paint); 437 } else { 438 addOp(alloc().create_trivial<ArcOp>( 439 Rect(left, top, right, bottom), 440 *(mState.currentSnapshot()->transform), 441 getRecordedClip(), 442 refPaint(&paint), 443 startAngle, sweepAngle, useCenter)); 444 } 445} 446 447void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) { 448 if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return; 449 450 addOp(alloc().create_trivial<PathOp>( 451 Rect(path.getBounds()), 452 *(mState.currentSnapshot()->transform), 453 getRecordedClip(), 454 refPaint(&paint), refPath(&path))); 455} 456 457void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { 458 mDisplayList->ref(tree); 459 mDisplayList->vectorDrawables.push_back(tree); 460 addOp(alloc().create_trivial<VectorDrawableOp>( 461 tree, 462 Rect(tree->stagingProperties()->getBounds()), 463 *(mState.currentSnapshot()->transform), 464 getRecordedClip())); 465} 466 467// Bitmap-based 468void RecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) { 469 save(SaveFlags::Matrix); 470 translate(left, top); 471 drawBitmap(bitmap, paint); 472 restore(); 473} 474 475void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, 476 const SkPaint* paint) { 477 if (matrix.isIdentity()) { 478 drawBitmap(bitmap, paint); 479 } else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) 480 && MathUtils::isPositive(matrix.getScaleX()) 481 && MathUtils::isPositive(matrix.getScaleY())) { 482 // SkMatrix::isScaleTranslate() not available in L 483 SkRect src; 484 SkRect dst; 485 bitmap.getBounds(&src); 486 matrix.mapRect(&dst, src); 487 drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, 488 dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint); 489 } else { 490 save(SaveFlags::Matrix); 491 concat(matrix); 492 drawBitmap(bitmap, paint); 493 restore(); 494 } 495} 496 497void RecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, 498 float srcRight, float srcBottom, float dstLeft, float dstTop, 499 float dstRight, float dstBottom, const SkPaint* paint) { 500 if (srcLeft == 0 && srcTop == 0 501 && srcRight == bitmap.width() 502 && srcBottom == bitmap.height() 503 && (srcBottom - srcTop == dstBottom - dstTop) 504 && (srcRight - srcLeft == dstRight - dstLeft)) { 505 // transform simple rect to rect drawing case into position bitmap ops, since they merge 506 save(SaveFlags::Matrix); 507 translate(dstLeft, dstTop); 508 drawBitmap(bitmap, paint); 509 restore(); 510 } else { 511 addOp(alloc().create_trivial<BitmapRectOp>( 512 Rect(dstLeft, dstTop, dstRight, dstBottom), 513 *(mState.currentSnapshot()->transform), 514 getRecordedClip(), 515 refPaint(paint), refBitmap(bitmap), 516 Rect(srcLeft, srcTop, srcRight, srcBottom))); 517 } 518} 519 520void RecordingCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, 521 const float* vertices, const int* colors, const SkPaint* paint) { 522 int vertexCount = (meshWidth + 1) * (meshHeight + 1); 523 addOp(alloc().create_trivial<BitmapMeshOp>( 524 calcBoundsOfPoints(vertices, vertexCount * 2), 525 *(mState.currentSnapshot()->transform), 526 getRecordedClip(), 527 refPaint(paint), refBitmap(bitmap), meshWidth, meshHeight, 528 refBuffer<float>(vertices, vertexCount * 2), // 2 floats per vertex 529 refBuffer<int>(colors, vertexCount))); // 1 color per vertex 530} 531 532void RecordingCanvas::drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& patch, 533 float dstLeft, float dstTop, float dstRight, float dstBottom, 534 const SkPaint* paint) { 535 addOp(alloc().create_trivial<PatchOp>( 536 Rect(dstLeft, dstTop, dstRight, dstBottom), 537 *(mState.currentSnapshot()->transform), 538 getRecordedClip(), 539 refPaint(paint), refBitmap(bitmap), refPatch(&patch))); 540} 541 542// Text 543void RecordingCanvas::drawGlyphs(const uint16_t* glyphs, const float* positions, int glyphCount, 544 const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, 545 float boundsRight, float boundsBottom, float totalAdvance) { 546 if (!glyphs || !positions || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return; 547 glyphs = refBuffer<glyph_t>(glyphs, glyphCount); 548 positions = refBuffer<float>(positions, glyphCount * 2); 549 550 // TODO: either must account for text shadow in bounds, or record separate ops for text shadows 551 addOp(alloc().create_trivial<TextOp>( 552 Rect(boundsLeft, boundsTop, boundsRight, boundsBottom), 553 *(mState.currentSnapshot()->transform), 554 getRecordedClip(), 555 refPaint(&paint), glyphs, positions, glyphCount, x, y)); 556 drawTextDecorations(x, y, totalAdvance, paint); 557} 558 559void RecordingCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset, 560 const SkPaint& paint, const SkPath& path, size_t start, size_t end) { 561 uint16_t glyphs[1]; 562 for (size_t i = start; i < end; i++) { 563 glyphs[0] = layout.getGlyphId(i); 564 float x = hOffset + layout.getX(i); 565 float y = vOffset + layout.getY(i); 566 if (PaintUtils::paintWillNotDrawText(paint)) return; 567 const uint16_t* tempGlyphs = refBuffer<glyph_t>(glyphs, 1); 568 addOp(alloc().create_trivial<TextOnPathOp>( 569 *(mState.currentSnapshot()->transform), 570 getRecordedClip(), 571 refPaint(&paint), tempGlyphs, 1, refPath(&path), x, y)); 572 } 573} 574 575void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkPaint* paint) { 576 addOp(alloc().create_trivial<BitmapOp>( 577 Rect(bitmap.width(), bitmap.height()), 578 *(mState.currentSnapshot()->transform), 579 getRecordedClip(), 580 refPaint(paint), refBitmap(bitmap))); 581} 582 583void RecordingCanvas::drawRenderNode(RenderNode* renderNode) { 584 auto&& stagingProps = renderNode->stagingProperties(); 585 RenderNodeOp* op = alloc().create_trivial<RenderNodeOp>( 586 Rect(stagingProps.getWidth(), stagingProps.getHeight()), 587 *(mState.currentSnapshot()->transform), 588 getRecordedClip(), 589 renderNode); 590 int opIndex = addOp(op); 591 if (CC_LIKELY(opIndex >= 0)) { 592 int childIndex = mDisplayList->addChild(op); 593 594 // update the chunk's child indices 595 DisplayList::Chunk& chunk = mDisplayList->chunks.back(); 596 chunk.endChildIndex = childIndex + 1; 597 598 if (renderNode->stagingProperties().isProjectionReceiver()) { 599 // use staging property, since recording on UI thread 600 mDisplayList->projectionReceiveIndex = opIndex; 601 } 602 } 603} 604 605void RecordingCanvas::drawLayer(DeferredLayerUpdater* layerHandle) { 606 // We ref the DeferredLayerUpdater due to its thread-safe ref-counting semantics. 607 mDisplayList->ref(layerHandle); 608 609 LOG_ALWAYS_FATAL_IF(layerHandle->backingLayer()->getApi() != Layer::Api::OpenGL); 610 // Note that the backing layer has *not* yet been updated, so don't trust 611 // its width, height, transform, etc...! 612 addOp(alloc().create_trivial<TextureLayerOp>( 613 Rect(layerHandle->getWidth(), layerHandle->getHeight()), 614 *(mState.currentSnapshot()->transform), 615 getRecordedClip(), 616 static_cast<GlLayer*>(layerHandle->backingLayer()))); 617} 618 619void RecordingCanvas::callDrawGLFunction(Functor* functor, 620 GlFunctorLifecycleListener* listener) { 621 mDisplayList->functors.push_back({functor, listener}); 622 mDisplayList->ref(listener); 623 addOp(alloc().create_trivial<FunctorOp>( 624 *(mState.currentSnapshot()->transform), 625 getRecordedClip(), 626 functor)); 627} 628 629int RecordingCanvas::addOp(RecordedOp* op) { 630 // skip op with empty clip 631 if (op->localClip && op->localClip->rect.isEmpty()) { 632 // NOTE: this rejection happens after op construction/content ref-ing, so content ref'd 633 // and held by renderthread isn't affected by clip rejection. 634 // Could rewind alloc here if desired, but callers would have to not touch op afterwards. 635 return -1; 636 } 637 638 int insertIndex = mDisplayList->ops.size(); 639 mDisplayList->ops.push_back(op); 640 if (mDeferredBarrierType != DeferredBarrierType::None) { 641 // op is first in new chunk 642 mDisplayList->chunks.emplace_back(); 643 DisplayList::Chunk& newChunk = mDisplayList->chunks.back(); 644 newChunk.beginOpIndex = insertIndex; 645 newChunk.endOpIndex = insertIndex + 1; 646 newChunk.reorderChildren = (mDeferredBarrierType == DeferredBarrierType::OutOfOrder); 647 newChunk.reorderClip = mDeferredBarrierClip; 648 649 int nextChildIndex = mDisplayList->children.size(); 650 newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex; 651 mDeferredBarrierType = DeferredBarrierType::None; 652 } else { 653 // standard case - append to existing chunk 654 mDisplayList->chunks.back().endOpIndex = insertIndex + 1; 655 } 656 return insertIndex; 657} 658 659void RecordingCanvas::refBitmapsInShader(const SkShader* shader) { 660 if (!shader) return; 661 662 // If this paint has an SkShader that has an SkBitmap add 663 // it to the bitmap pile 664 SkBitmap bitmap; 665 SkShader::TileMode xy[2]; 666 if (shader->isABitmap(&bitmap, nullptr, xy)) { 667 Bitmap* hwuiBitmap = static_cast<Bitmap*>(bitmap.pixelRef()); 668 refBitmap(*hwuiBitmap); 669 return; 670 } 671 SkShader::ComposeRec rec; 672 if (shader->asACompose(&rec)) { 673 refBitmapsInShader(rec.fShaderA); 674 refBitmapsInShader(rec.fShaderB); 675 return; 676 } 677} 678 679}; // namespace uirenderer 680}; // namespace android 681